Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDKS-3045 refactor next dont revoke when sso null #267

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion FRAuth/FRAuth.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@
D5F3E10324D20FBF00536EA0 /* SuspendedTextOutputCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F3E10224D20FBF00536EA0 /* SuspendedTextOutputCallback.swift */; };
D5F8F9EE24B7D50600EC9AEB /* BooleanAttributeInputCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8F9ED24B7D50600EC9AEB /* BooleanAttributeInputCallback.swift */; };
D5F8F9F024B7D85600EC9AEB /* NumberAttributeInputCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8F9EF24B7D85600EC9AEB /* NumberAttributeInputCallback.swift */; };
EC03A7902BA1F8AE00BF9711 /* NodeNext.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC03A78F2BA1F8AE00BF9711 /* NodeNext.swift */; };
EC03A7CB2BAB1C8200BF9711 /* PolicyAdviceUsernameNode.json in Resources */ = {isa = PBXBuildFile; fileRef = EC03A7C92BAB1C8200BF9711 /* PolicyAdviceUsernameNode.json */; };
EC0BA2E7285B8F8F00F8326E /* FROptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0BA2E6285B8F8F00F8326E /* FROptions.swift */; };
EC0BA2FA2863325B00F8326E /* FROptionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0BA2F92863325B00F8326E /* FROptionsTests.swift */; };
EC13ABAA29A380920069AC41 /* FRWebAuthnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC13ABA929A380920069AC41 /* FRWebAuthnManager.swift */; };
Expand Down Expand Up @@ -620,6 +622,8 @@
D5F8F9EF24B7D85600EC9AEB /* NumberAttributeInputCallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberAttributeInputCallback.swift; sourceTree = "<group>"; };
D5FBD8A224B930E30005DD0F /* BooleanAttributeInputCallbackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooleanAttributeInputCallbackTests.swift; sourceTree = "<group>"; };
D5FBD8A524B930F00005DD0F /* NumberAttributeInputCallbackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberAttributeInputCallbackTests.swift; sourceTree = "<group>"; };
EC03A78F2BA1F8AE00BF9711 /* NodeNext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeNext.swift; sourceTree = "<group>"; };
EC03A7C92BAB1C8200BF9711 /* PolicyAdviceUsernameNode.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = PolicyAdviceUsernameNode.json; sourceTree = "<group>"; };
EC0BA2E6285B8F8F00F8326E /* FROptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FROptions.swift; sourceTree = "<group>"; };
EC0BA2F92863325B00F8326E /* FROptionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FROptionsTests.swift; sourceTree = "<group>"; };
EC13ABA929A380920069AC41 /* FRWebAuthnManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FRWebAuthnManager.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1002,6 +1006,7 @@
children = (
D5723C3923F4C65800557AA8 /* AuthService.swift */,
D5723C3A23F4C65800557AA8 /* Node.swift */,
EC03A78F2BA1F8AE00BF9711 /* NodeNext.swift */,
);
path = Authentication;
sourceTree = "<group>";
Expand Down Expand Up @@ -1202,7 +1207,7 @@
959D7D97290B4B9200A1F22F /* AA-05-DeviceBindingCallbackTest.swift */,
95E180B32992A6F20087457D /* AA-06-DeviceSigningVerifierCallbackTest.swift */,
3A4B46E32AB95B3D009E7171 /* AA_07_AppIntegrityTest.swift */,
9536C56B2B865DD600B2DFDD /* AA_08_TextInputCallbackTest.swift */,
9536C56B2B865DD600B2DFDD /* AA_08_TextInputCallbackTest.swift */,
95EB7E4C2B8D010B00B59CD6 /* AA_09_PingOneProtectInitializeCallbackTest.swift */,
95EB7E522B8D5F6100B59CD6 /* AA_10_PingOneProtectEvaluateCallbackTest.swift */,
);
Expand Down Expand Up @@ -1345,6 +1350,7 @@
D5888C6C25664EDF0041FD94 /* AuthTree_DeviceCollectorNodeLocationOnly.json */,
D5888C6D25664EDF0041FD94 /* AuthTree_LoginNode.json */,
D5888C6E25664EDF0041FD94 /* AuthTree_UsernamePasswordNode.json */,
EC03A7C92BAB1C8200BF9711 /* PolicyAdviceUsernameNode.json */,
D5888C6F25664EDF0041FD94 /* AM_Push_Authentication_Successful.json */,
D5888C7025664EDF0041FD94 /* AuthTree_DeviceCollectorNodeWithMessage.json */,
D5888C7125664EDF0041FD94 /* AuthTree_RegistrationNode.json */,
Expand Down Expand Up @@ -1686,6 +1692,7 @@
D5888CA425664EDF0041FD94 /* AuthTree_UsernamePasswordNodeWithCustomCallback.json in Resources */,
D5888C9E25664EDF0041FD94 /* AuthTree_SSOToken_Success2.json in Resources */,
D5888C9B25664EDF0041FD94 /* OAuth2_EndSession_Failure.json in Resources */,
EC03A7CB2BAB1C8200BF9711 /* PolicyAdviceUsernameNode.json in Resources */,
D5888C9725664EDF0041FD94 /* OAuth2_UserInfo_Failure.json in Resources */,
D5888CAB25664EDF0041FD94 /* AuthTree_LoginNode.json in Resources */,
D5888CA225664EDF0041FD94 /* AM_Push_Authentication_Fail.json in Resources */,
Expand Down Expand Up @@ -1771,6 +1778,7 @@
D53A8044262789BD0093B1CA /* PlatformAuthenticatorConfig.swift in Sources */,
EC7EA0F427D10FA70003F878 /* AppleSignInUserStructs.swift in Sources */,
D533270B24806665002FF207 /* TokenManagementPolicy.swift in Sources */,
EC03A7902BA1F8AE00BF9711 /* NodeNext.swift in Sources */,
D586CF9D23358EE0007A2194 /* AbstractValidatedCallback.swift in Sources */,
D586CFBD23358EE0007A2194 /* FRDeviceIdentifier.swift in Sources */,
D586CF9723358EE0007A2194 /* SingleValueCallback.swift in Sources */,
Expand Down
194 changes: 21 additions & 173 deletions FRAuth/FRAuth/Authentication/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// AuthService.swift
// FRAuth
//
// Copyright (c) 2019-2021 ForgeRock. All rights reserved.
// Copyright (c) 2019-2024 ForgeRock. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
Expand All @@ -22,25 +22,7 @@ import FRCore
* Any custom Callback must be implemented by inheriting Callback class, and be registered through CallbackFactory.shared.registerCallback(callbackType:callbackClass:).
*/
@objc(FRAuthService)
public class AuthService: NSObject {

// MARK: - Property

/// String value of AuthService name registered in AM
@objc public internal(set) var serviceName: String
/// Unique UUID String value of initiated AuthService flow
@objc public internal(set) var authServiceId: String

/// authIndexType value in AM
var authIndexType: String
/// ServerConfig that contains OpenAM server information
var serverConfig: ServerConfig
/// OAuth2CLient that contains OpenAM's OAuth2 client information
var oAuth2Config: OAuth2Client?
/// TokenManager instance to manage, and persist authenticated session
var tokenManager: TokenManager?
/// KeychainManager instance to persist, and retrieve credentials from storage
var keychainManager: KeychainManager?
public class AuthService: NodeNext {


// MARK: - Init
Expand All @@ -52,6 +34,8 @@ public class AuthService: NSObject {
/// - serverConfig: ServerConfig object for AuthService server communication
@objc
public init(name: String, serverConfig: ServerConfig) {
super.init()

FRLog.v("AuthService init - service: \(name), ServerConfig: \(serverConfig)")
self.serviceName = name
self.authIndexType = OpenAM.service
Expand All @@ -68,6 +52,8 @@ public class AuthService: NSObject {
/// - keychainManager: KeychainManager instance to persist, and retrieve credentials from secure storage
/// - tokenManager: TokenManager instance to manage and persist authenticated session
init(suspendedId: String, serverConfig: ServerConfig, oAuth2Config: OAuth2Client?, keychainManager: KeychainManager? = nil, tokenManager: TokenManager? = nil) {
super.init()

FRLog.v("AuthService init - suspendedId: \(suspendedId), ServerConfig: \(serverConfig), OAuth2Client: \(String(describing: oAuth2Config)), KeychainManager: \(String(describing: keychainManager)), TokenManager: \(String(describing: tokenManager))")
self.serviceName = suspendedId
self.authIndexType = OpenAM.suspendedId
Expand All @@ -90,6 +76,8 @@ public class AuthService: NSObject {
/// - tokenManager: TokenManager instance to manage and persist authenticated session
/// - authIndexType: String value of Authentication Tree type
init(authIndexValue: String, serverConfig: ServerConfig, oAuth2Config: OAuth2Client?, keychainManager: KeychainManager? = nil, tokenManager: TokenManager? = nil, authIndexType: String = OpenAM.service) {
super.init()

FRLog.v("AuthService init - service: \(authIndexValue), serviceType: \(authIndexType) ServerConfig: \(serverConfig), OAuth2Client: \(String(describing: oAuth2Config)), KeychainManager: \(String(describing: keychainManager)), TokenManager: \(String(describing: tokenManager))")
self.serviceName = authIndexValue
self.authIndexType = authIndexType
Expand All @@ -101,168 +89,27 @@ public class AuthService: NSObject {
}


// MARK: Public

/// Submits current Node object with Callback(s) and its given value(s) to OpenAM to proceed on authentication flow.
///
/// - Parameter completion: NodeCompletion callback which returns the result of Node submission.
public func next<T>(completion: @escaping NodeCompletion<T>) {

if T.self as AnyObject? === Token.self {
next { (token: Token?, node, error) in
completion(token as? T, node, error)
}
}
else if T.self as AnyObject? === AccessToken.self {
next { (token: AccessToken?, node, error) in
completion(token as? T, node, error)
}
}
else if T.self as AnyObject? === FRUser.self {
next { (user: FRUser?, node, error) in
completion(user as? T, node, error)
}
}
else {
completion(nil, nil, AuthError.invalidGenericType)
}
}


// MARK: Private/internal methods to handle different expected type of result

fileprivate func next(completion: @escaping NodeCompletion<FRUser>) {
if let currentUser = FRUser.currentUser, currentUser.token != nil {
FRLog.i("FRUser.currentUser retrieved from SessionManager; ignoring AuthService submit")
completion(currentUser, nil, nil)
}
else {
self.next { (accessToken: AccessToken?, node, error) in
if let token = accessToken {
let user = FRUser(token: token)

completion(user, nil, nil)
}
else {
completion(nil, node, error)
}
}
}
}


fileprivate func next(completion: @escaping NodeCompletion<AccessToken>) {

if let accessToken = try? self.keychainManager?.getAccessToken() {
FRLog.i("access_token retrieved from SessionManager; ignoring AuthService submit")
completion(accessToken, nil, nil)
}
else {
self.next { (token: Token?, node, error) in

if let tokenId = token {
// If OAuth2Client is provided (for abstraction layer)
if let oAuth2Client = self.oAuth2Config {
// Exchange 'tokenId' (SSOToken) to OAuth2 token set
oAuth2Client.exchangeToken(token: tokenId, completion: { (accessToken, error) in
// Return an error if failed
if let error = error {
completion(nil, nil, error)
}
else {

if let token = accessToken {
do {
try self.keychainManager?.setAccessToken(token: token)
}
catch {
FRLog.e("Unexpected error while storing AccessToken: \(error.localizedDescription)")
}
}

// Return AccessToken
completion(accessToken, nil, nil)
}
})
}
else {
completion(nil, nil, AuthError.invalidOAuth2Client)
}
}
else {
completion(nil, node, error)
}
}
}
}


fileprivate func next(completion: @escaping NodeCompletion<Token>) {
override func next(completion: @escaping NodeCompletion<Token>) {

// Construct Request object for AuthService flow with given serviceName
let request = self.buildAuthServiceRequest()
guard let request = try? self.buildAuthServiceRequest(),
let authServiceId = self.authServiceId,
let serverConfig = self.serverConfig,
let serviceName = self.serviceName,
let authIndexType = self.authIndexType
else { return }

var action: Action?
let action: Action
// For /authenticate request with suspendedId, return .RESUME_AUTHENTICATE type
if self.authIndexType == OpenAM.suspendedId {
action = Action(type: .RESUME_AUTHENTICATE)
}
// Otherwise, regular .START_AUTHENTICATE Action type
else {
action = Action(type: .START_AUTHENTICATE, payload: ["tree": self.serviceName, "type": self.authIndexType])
action = Action(type: .START_AUTHENTICATE, payload: ["tree": serviceName, "type": authIndexType])
}

// Invoke request
FRRestClient.invoke(request: request, action: action) { (result) in
switch result {
case .success(let response, _):

// If authId received
if let _ = response[OpenAM.authId] {
do {
let node = try Node(self.authServiceId, response, self.serverConfig, self.serviceName, self.authIndexType, self.oAuth2Config, self.keychainManager, self.tokenManager)
completion(nil, node, nil)
} catch let authError as AuthError {
completion(nil, nil, authError)
} catch {
completion(nil, nil, error)
}
}
else if let tokenId = response[OpenAM.tokenId] as? String {
let token = Token(tokenId)
if let keychainManager = self.keychainManager {
let currentSessionToken = keychainManager.getSSOToken()
if let _ = try? keychainManager.getAccessToken(), token.value != currentSessionToken?.value {
FRLog.w("SDK identified existing Session Token (\(currentSessionToken?.value ?? "nil")) and received Session Token (\(token.value))'s mismatch; to avoid misled information, SDK automatically revokes OAuth2 token set issued with existing Session Token.")
if let tokenManager = self.tokenManager {
tokenManager.revokeAndEndSession { (error) in
FRLog.i("OAuth2 token set was revoked due to mismatch of Session Tokens; \(error?.localizedDescription ?? "")")
}
}
else {
FRLog.i("TokenManager is not found; OAuth2 token set was removed from the storage")
do {
try keychainManager.setAccessToken(token: nil)
}
catch {
FRLog.e("Unexpected error while removing AccessToken: \(error.localizedDescription)")
}
}
}
keychainManager.setSSOToken(ssoToken: token)
}

completion(token, nil, nil)
}
else {
completion(nil, nil, nil)
}
break
case .failure(let error):
completion(nil, nil, error)
break
}
}
self.invoke(authServiceId: authServiceId, serverConfig: serverConfig, serviceName: serviceName, authIndexType: authIndexType, request: request, action: action, completion: completion)
}


Expand All @@ -271,12 +118,13 @@ public class AuthService: NSObject {
/// Builds Request object for current Node
///
/// - Returns: Request object for OpenAM AuthTree submit
func buildAuthServiceRequest() -> Request {
override func buildAuthServiceRequest() throws -> Request {

// AM 6.5.2 - 7.0.0
//
// Endpoint: /json/realms/authenticate
// API Version: resource=2.1,protocol=1.0
guard let serverConfig = self.serverConfig else { throw ConfigError.invalidSDKState }

var header: [String: String] = [:]
header[OpenAM.acceptAPIVersion] = OpenAM.apiResource21 + "," + OpenAM.apiProtocol10
Expand All @@ -292,7 +140,7 @@ public class AuthService: NSObject {
parameter[OpenAM.authIndexValue] = self.serviceName
}

return Request(url: self.serverConfig.authenticateURL, method: .POST, headers: header, urlParams: parameter, requestType: .json, responseType: .json, timeoutInterval: self.serverConfig.timeout)
return Request(url: serverConfig.authenticateURL, method: .POST, headers: header, urlParams: parameter, requestType: .json, responseType: .json, timeoutInterval: serverConfig.timeout)
}


Expand Down
Loading
Loading