diff --git a/src/main/java/org/fireflyframework/idp/adapter/IdpAdapter.java b/src/main/java/org/fireflyframework/idp/adapter/IdpAdapter.java index 76ebfae..e6cd03e 100644 --- a/src/main/java/org/fireflyframework/idp/adapter/IdpAdapter.java +++ b/src/main/java/org/fireflyframework/idp/adapter/IdpAdapter.java @@ -17,186 +17,39 @@ package org.fireflyframework.idp.adapter; -import org.fireflyframework.idp.dtos.*; +import org.fireflyframework.idp.dtos.CreateUserRequest; +import org.fireflyframework.idp.dtos.CreateUserResponse; +import org.fireflyframework.idp.dtos.RegisterUserRequest; +import org.fireflyframework.idp.port.AuthenticationPort; +import org.fireflyframework.idp.port.MfaPort; +import org.fireflyframework.idp.port.RoleScopePort; +import org.fireflyframework.idp.port.SessionPort; +import org.fireflyframework.idp.port.TokenIntrospectionPort; +import org.fireflyframework.idp.port.UserAdminPort; import org.springframework.http.ResponseEntity; import reactor.core.publisher.Mono; -import java.util.List; - /** - * Identity Provider (IdP) adapter interface that standardizes common authentication and - * user‑management operations across different IdPs such as Keycloak, AWS Cognito, Okta, etc. + * Identity Provider (IdP) adapter — the aggregate of the segregated capability ports + * ({@link AuthenticationPort}, {@link TokenIntrospectionPort}, {@link UserAdminPort}, + * {@link RoleScopePort}, {@link SessionPort}, {@link MfaPort}) that standardizes authentication and + * user-management across IdPs such as Keycloak, AWS Cognito, and Entra ID. * - * Implementations should translate these generic operations into the specific API calls of the - * target provider while keeping a consistent, reactive API based on Reactor's Mono. + *

Because every capability-port method has a {@code NotSupported} default, an adapter may + * implement this aggregate and override only the operations its provider supports, or implement a + * single focused port directly. This keeps providers from stubbing capabilities they cannot perform + * (the previous fat-interface ISP violation). */ -public interface IdpAdapter { - - /** - * Authenticate a user and obtain tokens. - * - * Typical mapping is an OAuth2/OIDC token endpoint using Resource Owner Password Credentials, - * or any custom username/password flow supported by the provider. - * - * @param request credentials and client details for the login attempt - * @return a reactive publisher yielding a ResponseEntity with a TokenResponse on success - */ - Mono> login(LoginRequest request); - - /** - * Refresh the access token using a refresh token. - * - * @param request refresh token and (optionally) client info - * @return a reactive publisher with a ResponseEntity containing a new TokenResponse - */ - Mono> refresh(RefreshRequest request); - - /** - * Invalidate tokens or perform a provider-specific logout. - * - * Implementations may call the IdP's logout or token revocation endpoint. - * - * @param request contains accessToken and refreshToken to invalidate - */ - Mono logout(LogoutRequest request); - - /** - * Introspect an access token to verify its activity and claims (RFC 7662). - * - * @param accessToken the token to introspect - * @return a reactive publisher with a ResponseEntity containing introspection details - */ - Mono> introspect(String accessToken); - - /** - * Retrieve OpenID Connect user info associated with the provided access token. - * - * @param accessToken a valid access token - * @return a reactive publisher with a ResponseEntity containing user info claims - */ - Mono> getUserInfo(String accessToken); - - /** - * Create a new user at the identity provider. - * - * @param request details required to create the user (username, email, etc.) - * @return a reactive publisher with a ResponseEntity containing the created user's summary - */ - Mono> createUser(CreateUserRequest request); - - /** - * Change a user's password. - * - * Implementations should enforce provider-specific requirements (e.g., old password check). - * - * @param request contains user identifier and new/old passwords - */ - Mono changePassword(ChangePasswordRequest request); - - /** - * Trigger a password reset for a given username (e.g., send reset email/SMS). - * - * @param username the username (or login identifier) for which to initiate reset - */ - Mono resetPassword(String username); - - /** - * Initiate a Multi‑Factor Authentication (MFA) challenge for a user. - * - * @param username the username to challenge - * @return a reactive publisher with challenge details (delivery method, expiry, etc.) - */ - Mono> mfaChallenge(String username); - - /** - * Verify a previously initiated MFA challenge with the supplied code. - * - * @param request verification payload including challenge id and code - */ - Mono mfaVerify(MfaVerifyRequest request); - - /** - * Revoke an issued refresh token. - * - * @param refreshToken the refresh token to revoke - */ - Mono revokeRefreshToken(String refreshToken); - - /** - * List active sessions for a given user. - * - * @param userId the user identifier - * @return a reactive publisher with a list of session information - */ - Mono>> listSessions(String userId); - - /** - * Revoke a specific session by its identifier. - * - * @param sessionId the session identifier to revoke - */ - Mono revokeSession(String sessionId); - - /** - * Retrieve the roles assigned to a user. - * - * @param userId the user identifier - * @return a reactive publisher with the list of role names - */ - Mono>> getRoles(String userId); - - /** - * Delete a user by its identifier in the IdP. - * - * @param userId the provider-specific user id - */ - Mono deleteUser(String userId); - - /** - * Update an existing user. Fields left null in the request should not be modified. - * - * @param request details to update and user identifier - * @return a reactive publisher with a summary of the updated user - */ - Mono> updateUser(UpdateUserRequest request); - - /** - * Create one or more roles at the IdP (realm or client depending on context). - * - * @param request role names (and optional context/description) - * @return a reactive publisher with the list of created role names - */ - Mono> createRoles(CreateRolesRequest request); - - /** - * Create a new scope (e.g., OAuth2 scope or Keycloak client scope). - * - * @param request scope name (and optional context/description) - * @return a reactive publisher with created scope information - */ - Mono> createScope(CreateScopeRequest request); - - /** - * Assign roles to a user. - * - * @param request user id and role names to assign - */ - Mono assignRolesToUser(AssignRolesRequest request); - - /** - * Remove roles from a user. - * - * @param request user id and role names to remove - */ - Mono removeRolesFromUser(AssignRolesRequest request); +public interface IdpAdapter + extends AuthenticationPort, TokenIntrospectionPort, UserAdminPort, RoleScopePort, SessionPort, MfaPort { /** * Register a new user via self-service (public, no admin auth required). * *

The default implementation converts the registration request into a - * {@link CreateUserRequest} and delegates to {@link #createUser(CreateUserRequest)}. + * {@link CreateUserRequest} and delegates to {@link UserAdminPort#createUser(CreateUserRequest)}. * Adapters may override this to use provider-specific self-service APIs - * (e.g., Cognito {@code SignUp}, Keycloak self-registration). + * (e.g. Cognito {@code SignUp}, Keycloak self-registration). * * @param request the self-service registration details * @return a reactive publisher with the created user's summary diff --git a/src/main/java/org/fireflyframework/idp/dtos/CreateUserRequest.java b/src/main/java/org/fireflyframework/idp/dtos/CreateUserRequest.java index fc0c0e5..1efe23e 100644 --- a/src/main/java/org/fireflyframework/idp/dtos/CreateUserRequest.java +++ b/src/main/java/org/fireflyframework/idp/dtos/CreateUserRequest.java @@ -17,15 +17,18 @@ package org.fireflyframework.idp.dtos; -import org.fireflyframework.idp.dtos.enums.UserRoleEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Map; -import java.util.UUID; +/** + * Vendor-neutral request to provision a user. Product-specific identity attributes (e.g. a party + * or contract reference) are carried generically in {@link #attributes} — the framework defines no + * product domain of its own. + */ @Data @Builder @NoArgsConstructor @@ -36,7 +39,5 @@ public class CreateUserRequest { private String password; private String givenName; private String familyName; - private UUID partyId; - private UserRoleEnum userRole; private Map attributes; } diff --git a/src/main/java/org/fireflyframework/idp/dtos/IntrospectionResponse.java b/src/main/java/org/fireflyframework/idp/dtos/IntrospectionResponse.java index aa01ffd..c5e4df3 100644 --- a/src/main/java/org/fireflyframework/idp/dtos/IntrospectionResponse.java +++ b/src/main/java/org/fireflyframework/idp/dtos/IntrospectionResponse.java @@ -18,15 +18,18 @@ package org.fireflyframework.idp.dtos; import com.fasterxml.jackson.annotation.JsonFormat; -import org.fireflyframework.idp.dtos.enums.UserRoleEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; -import java.util.UUID; +import java.util.Map; +/** + * RFC 7662 token introspection response. Product-specific claims are returned generically in + * {@link #attributes}; the framework keeps no product domain (no party, contract, or business role). + */ @Data @Builder @NoArgsConstructor @@ -42,6 +45,5 @@ public class IntrospectionResponse { private List aud; private String iss; private String jti; - private UUID partyId; - private UserRoleEnum userRole; + private Map attributes; } diff --git a/src/main/java/org/fireflyframework/idp/dtos/enums/UserRoleEnum.java b/src/main/java/org/fireflyframework/idp/dtos/enums/UserRoleEnum.java deleted file mode 100644 index 464bf60..0000000 --- a/src/main/java/org/fireflyframework/idp/dtos/enums/UserRoleEnum.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.fireflyframework.idp.dtos.enums; - -public enum UserRoleEnum { - AGENT, - SUPER_AGENT, - DISTRIBUTOR, - ADMIN -} diff --git a/src/main/java/org/fireflyframework/idp/port/AuthenticationPort.java b/src/main/java/org/fireflyframework/idp/port/AuthenticationPort.java new file mode 100644 index 0000000..14de044 --- /dev/null +++ b/src/main/java/org/fireflyframework/idp/port/AuthenticationPort.java @@ -0,0 +1,46 @@ +/* + * Copyright 2024-2026 Firefly Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.fireflyframework.idp.port; + +import org.fireflyframework.idp.dtos.LoginRequest; +import org.fireflyframework.idp.dtos.LogoutRequest; +import org.fireflyframework.idp.dtos.RefreshRequest; +import org.fireflyframework.idp.dtos.TokenResponse; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +/** + * Segregated IdP capability port for authentication / token-lifecycle operations. + * + *

Each method has a {@code NotSupported} default so an adapter implements only the capabilities + * its provider actually offers, instead of stubbing the rest. The aggregate {@code IdpAdapter} + * composes this with the other capability ports. + */ +public interface AuthenticationPort { + + default Mono> login(LoginRequest request) { + return Mono.error(new UnsupportedOperationException("login is not supported by this IdP provider")); + } + + default Mono> refresh(RefreshRequest request) { + return Mono.error(new UnsupportedOperationException("refresh is not supported by this IdP provider")); + } + + default Mono logout(LogoutRequest request) { + return Mono.error(new UnsupportedOperationException("logout is not supported by this IdP provider")); + } +} diff --git a/src/main/java/org/fireflyframework/idp/port/MfaPort.java b/src/main/java/org/fireflyframework/idp/port/MfaPort.java new file mode 100644 index 0000000..1e43793 --- /dev/null +++ b/src/main/java/org/fireflyframework/idp/port/MfaPort.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024-2026 Firefly Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.fireflyframework.idp.port; + +import org.fireflyframework.idp.dtos.MfaChallengeResponse; +import org.fireflyframework.idp.dtos.MfaVerifyRequest; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +/** + * Segregated IdP capability port for multi-factor authentication challenge/verify. + */ +public interface MfaPort { + + default Mono> mfaChallenge(String username) { + return Mono.error(new UnsupportedOperationException("mfaChallenge is not supported by this IdP provider")); + } + + default Mono mfaVerify(MfaVerifyRequest request) { + return Mono.error(new UnsupportedOperationException("mfaVerify is not supported by this IdP provider")); + } +} diff --git a/src/main/java/org/fireflyframework/idp/port/RoleScopePort.java b/src/main/java/org/fireflyframework/idp/port/RoleScopePort.java new file mode 100644 index 0000000..e1b4a5a --- /dev/null +++ b/src/main/java/org/fireflyframework/idp/port/RoleScopePort.java @@ -0,0 +1,53 @@ +/* + * Copyright 2024-2026 Firefly Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.fireflyframework.idp.port; + +import org.fireflyframework.idp.dtos.AssignRolesRequest; +import org.fireflyframework.idp.dtos.CreateRolesRequest; +import org.fireflyframework.idp.dtos.CreateRolesResponse; +import org.fireflyframework.idp.dtos.CreateScopeRequest; +import org.fireflyframework.idp.dtos.CreateScopeResponse; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +import java.util.List; + +/** + * Segregated IdP capability port for role and scope management and role assignment. + */ +public interface RoleScopePort { + + default Mono>> getRoles(String userId) { + return Mono.error(new UnsupportedOperationException("getRoles is not supported by this IdP provider")); + } + + default Mono> createRoles(CreateRolesRequest request) { + return Mono.error(new UnsupportedOperationException("createRoles is not supported by this IdP provider")); + } + + default Mono> createScope(CreateScopeRequest request) { + return Mono.error(new UnsupportedOperationException("createScope is not supported by this IdP provider")); + } + + default Mono assignRolesToUser(AssignRolesRequest request) { + return Mono.error(new UnsupportedOperationException("assignRolesToUser is not supported by this IdP provider")); + } + + default Mono removeRolesFromUser(AssignRolesRequest request) { + return Mono.error(new UnsupportedOperationException("removeRolesFromUser is not supported by this IdP provider")); + } +} diff --git a/src/main/java/org/fireflyframework/idp/port/SessionPort.java b/src/main/java/org/fireflyframework/idp/port/SessionPort.java new file mode 100644 index 0000000..5cf2038 --- /dev/null +++ b/src/main/java/org/fireflyframework/idp/port/SessionPort.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024-2026 Firefly Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.fireflyframework.idp.port; + +import org.fireflyframework.idp.dtos.SessionInfo; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +import java.util.List; + +/** + * Segregated IdP capability port for session and refresh-token lifecycle management. + */ +public interface SessionPort { + + default Mono revokeRefreshToken(String refreshToken) { + return Mono.error(new UnsupportedOperationException("revokeRefreshToken is not supported by this IdP provider")); + } + + default Mono>> listSessions(String userId) { + return Mono.error(new UnsupportedOperationException("listSessions is not supported by this IdP provider")); + } + + default Mono revokeSession(String sessionId) { + return Mono.error(new UnsupportedOperationException("revokeSession is not supported by this IdP provider")); + } +} diff --git a/src/main/java/org/fireflyframework/idp/port/TokenIntrospectionPort.java b/src/main/java/org/fireflyframework/idp/port/TokenIntrospectionPort.java new file mode 100644 index 0000000..23d79be --- /dev/null +++ b/src/main/java/org/fireflyframework/idp/port/TokenIntrospectionPort.java @@ -0,0 +1,37 @@ +/* + * Copyright 2024-2026 Firefly Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.fireflyframework.idp.port; + +import org.fireflyframework.idp.dtos.IntrospectionResponse; +import org.fireflyframework.idp.dtos.UserInfoResponse; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +/** + * Segregated IdP capability port for token introspection (RFC 7662) and OIDC userinfo. This is the + * provider-facing source behind the security tier's opaque-token validation path. + */ +public interface TokenIntrospectionPort { + + default Mono> introspect(String accessToken) { + return Mono.error(new UnsupportedOperationException("introspect is not supported by this IdP provider")); + } + + default Mono> getUserInfo(String accessToken) { + return Mono.error(new UnsupportedOperationException("getUserInfo is not supported by this IdP provider")); + } +} diff --git a/src/main/java/org/fireflyframework/idp/port/UserAdminPort.java b/src/main/java/org/fireflyframework/idp/port/UserAdminPort.java new file mode 100644 index 0000000..4030293 --- /dev/null +++ b/src/main/java/org/fireflyframework/idp/port/UserAdminPort.java @@ -0,0 +1,51 @@ +/* + * Copyright 2024-2026 Firefly Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.fireflyframework.idp.port; + +import org.fireflyframework.idp.dtos.ChangePasswordRequest; +import org.fireflyframework.idp.dtos.CreateUserRequest; +import org.fireflyframework.idp.dtos.CreateUserResponse; +import org.fireflyframework.idp.dtos.UpdateUserRequest; +import org.fireflyframework.idp.dtos.UpdateUserResponse; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +/** + * Segregated IdP capability port for user administration (CRUD + password operations). + */ +public interface UserAdminPort { + + default Mono> createUser(CreateUserRequest request) { + return Mono.error(new UnsupportedOperationException("createUser is not supported by this IdP provider")); + } + + default Mono changePassword(ChangePasswordRequest request) { + return Mono.error(new UnsupportedOperationException("changePassword is not supported by this IdP provider")); + } + + default Mono resetPassword(String username) { + return Mono.error(new UnsupportedOperationException("resetPassword is not supported by this IdP provider")); + } + + default Mono deleteUser(String userId) { + return Mono.error(new UnsupportedOperationException("deleteUser is not supported by this IdP provider")); + } + + default Mono> updateUser(UpdateUserRequest request) { + return Mono.error(new UnsupportedOperationException("updateUser is not supported by this IdP provider")); + } +}