Skip to content

Midships 4.7.0#320

Open
yuxiang-midships wants to merge 8 commits intoForgeRock:developfrom
yuxiang-midships:midships-4.7.0
Open

Midships 4.7.0#320
yuxiang-midships wants to merge 8 commits intoForgeRock:developfrom
yuxiang-midships:midships-4.7.0

Conversation

@yuxiang-midships
Copy link

Hi Ping SDK Team,

Yuxiang here from Midships. I am raising this pull request to show some changes we are doing to support some of our clients for Ping SDK integration. These change on iOS SDK is to allow certain feature available on the Android SDK to be also available on the iOS SDK.

Summary:

  1. expose the token manager interface and instance on iOS sdk
  2. expose the clear method in token manager on the iOS sdk
  3. add the persist token method in token manager on the iOS sdk

Feel free to reach out to me for more details and why some clients will need these changes.

Best regards
Yuxiang
Associate Director, Midships

spetrov and others added 7 commits April 25, 2024 11:51
Fixed privacy manifest file in the SDK's modules
Similar to android SDK 's Config.getInstance().getTokenManager() to get token manager instance
Similar to Android SDK's TokenManger.clear() method
Similar to Android SDK's TokenManger.persist(AccessToken) method
@george-bafaloukas-forgerock
Copy link
Contributor

Hi @yuxiang-midships thanks for raising this PR.

Can you please provide more information on the use cases you want to cover by making the Token manager and the "persist token method" public?

@yuxiang-midships
Copy link
Author

#320 (comment)

Hi George,

Thank you for taking a look at this. We are using FRSession instead of FRuser to have more control over different authentication tree through out the mobile apple CIAM journeys. At the same time, we have Oauth2 flows that will need to be supported. For the android SDK, we can get around and achieve the logic we want as we can access:

Config.getInstance().getSessionManager().getTokenManager()

which allow us to access on android the tokenManager.

We can then use the exchangeToken, clear and persist method in the token manager class to achieve using FR session for invoking AM trees and then use the tokenID to do oauth2 flows.

However when it come to the iOS SDK, we are not able to access the token manager and use the equivalent method. Therefore we modified the iOS SDK as illustrated in the commits to allow iOS sdk to have the same feature as the android SDK

We make the token manager and the ouath2 client in the token manager public. and we also make the clearCredential method in token manager public. Lastly we added the persist method to the token manager.

@george-bafaloukas-forgerock
Copy link
Contributor

Hi there,

Thanks for your reply. I will be actively reviewing this on the current Sprint. I am assuming this is related with the support case: 01076030 as well. Please expect more questions as part of that :)

@george-bafaloukas-forgerock
Copy link
Contributor

george-bafaloukas-forgerock commented Feb 19, 2025

Hey @yuxiang-midships and team, one more clarification.
You mention

We are using FRSession instead of FRuser to have more control over different authentication tree through out the mobile apple CIAM journeys

This is correct through the use of FRSession.authenticate you can control the use of the authentication tree. At the end of the authentication you can then jump on the FRUser classes. For example check out the following code:

func handleNode(token: Token?, node: Node?, error: Error?) {
        if (token == nil) && (node == nil) && (error == nil) {
            print("All nil indicates a noSession=true completed Journey")
            self.updateStatus()
            return
        }
        if error != nil {
            print(error!)
            return
        }
        self.currentNode = node
        if let _ = token {
            print("Session only Journey is now complete")
            FRUser.currentUser?.getAccessToken(completion: { user, error in
                DispatchQueue.main.async {
                    self.updateStatus()
                }
            })
        } else {
            if node == nil {
                return
            }
            for callback: Callback in node!.callbacks {
            }
        }
    }

So basically after getting the Token you can work with the FRUser interface which should expose all the methods required. Please let me know if I am missing something here. The only possible reason I can think of exposing more from the TokenManager would be the use of different OAuth2.0 flows other than the one the SDK supports OOTB.

Same logic applies to Android

@yuxiang-midships
Copy link
Author

yuxiang-midships commented Feb 19, 2025

Hi George,

You are absolutely correct that after using FRSession, we can use FRUser to exchange for Oauth2 Access token. However two of our clients are also using AM trees to carryout extra authentication at a transaction level (not session level). In the flow we implemented, we have custom OAuth 2 scripts that enable issuance of special scoped access token where the mobile end can use to invoke certain high risk transactions if the customer pass certain trees.

The issue we faced is that when FRsession finish an AM tree, upon receiving a new SSO Token, the SDK behaviour will always override the existing session with the new session, similarly when using FRUser to exchange for token, new access token will override the existing access token.

This behaviour is not we wanted, because we want to retain the session level access token (we dont care about the SSO token, it can be forgotten after access token exchange). So we manage to find a work around by using the Token manger interface in android where before we invoke the special transaction level AM trees, we first cache the session level access token to memory and then invoke the clear credential method. This makes the SDK entering a clean state. After the special transaction level AM tree is finished via FRSession and node handing, we exchange for the special scoped token directly via tokenmanger.oauth2client and give it to the mobile app for a one time use (this access token do not enter keychain) and then we set back the cached session level access token back to the SDK via the token manager interface persist method. This allows the mobile app to continue to use the session level access token for other non risky APIs

This workaround can be achieved without any SDK modification in android, but in IOS we have to make a few lines of code change as shown in this PR.

Also some additional things that we manage to achieve with this workaround

  1. our client do not use OIDC session management, but they do want to have the IDToken. However when doing elevation flows (one AM tree and then another AM tree at some point in time in the customer journey), the OIDC session logout is always invoked, creating unnecessary calls to AM. In this workaround we did by using FRSession + the token-manager interface, we eliminated that call.

  2. Consistency on iOS and android SDK implementation. Because android SDK support this workaround, our clients want to have the similar implementation on the iOS SDK too. (both of these 2 clients use react native). By making the modification shown in this PR, we are able to make the iOS and the android SDK implementation largely the same and expose the same bridging methods to react native to consume.

  3. Overall easiness for mobile team to understand the SDK integration implementation. As the Ping SDK integration implementation is still effectively owned by the mobile team, by using only FRSession and nodes to handle AM tree, and token manager to handle Oauth2 flows, this avoided confusion in the mobile team. In both of the clients, all they want to achieve is to use the SDK to handle the AM callbacks, then get the JWT tokens. Both clients do not call CIAM for token introspect or policy evaluation, they have their own implementation on PEP that take in JWT token and do local introspect of JTW token by keeping the CIAM public key. The current SDK behaviour around revoking the access token, logging out the session automatically within FRUser and FRUser actually create blockage on what the client want to achieve with CIAM.

On midships's point of view, we understand why the SDK has implemented these logic. Therefore rather than modifying those logic at FRUser or FRSession level, we just modified certain interface/method to be exposed (in this case the token manager) such that the client can build their own Ping SDK wrapper classes using those interface and methods to bring out the behaviour they want (not alot of effort as most of the complicated things are still solved by the SDK). In our perspective, this will allow client that have very standard use cases to just use FRUser and FR session. For clients that have more non-standard use cases, they can use FR session for interaction with AM tress and token manger to handle Oauth/ODIC/refreshing of access token/storing of access token to secured storage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @yuxiang-midships and team, thank you very much for submitting this request and providing these clarifications.

Can I please ask you to have a look on the comments and also make sure that any follow up commits are done using "Verified" account status. Please let us know if you need details on how to set up a "Verified" key.

Last but not least, can you please add some tests for the proposed changes?

Kind regards,
George Bafaloukas

public struct TokenManager {

var oAuth2Client: OAuth2Client
public var oAuth2Client: OAuth2Client

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add code doc for the public oAuth2Client

Browser.currentBrowser = nil
}

public func persistToken(token: AccessToken) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add code doc for the persistToken method

Copy link
Contributor

@vahancouver vahancouver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments to address

PS: the commits need to be signed


/// TokenManager class is a management class responsible for persisting, retrieving, and refreshing OAuth2 token(s)
struct TokenManager {
public struct TokenManager {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the copyright header to reflect the latest changes year

var oAuth2Client: OAuth2Client?
/// TokenManager instance for FRAuth to perform any token related operation
var tokenManager: TokenManager?
public var tokenManager: TokenManager?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the copyright header to reflect the latest changes year

/// - oAuth2Client: OAuth2Client instance for OAuth2 token protocols
/// - keychainManager: KeychainManager instance for secure credentials management
public init(oAuth2Client: OAuth2Client, keychainManager: KeychainManager) {
init(oAuth2Client: OAuth2Client, keychainManager: KeychainManager) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was public meant to be removed here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants