Skip to content

Commit

Permalink
Merge pull request #228 from ForgeRock/develop
Browse files Browse the repository at this point in the history
ForgeRock iOS SDK 4.1.0 Release
  • Loading branch information
vahancouver authored Jul 27, 2023
2 parents d78e9f2 + 4f7904e commit f797c8c
Show file tree
Hide file tree
Showing 50 changed files with 1,043 additions and 225 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
# Version 4.0.0
# Version 4.1.0

## [4.1.0]
#### Added
- Interceptor support for the Authenticator module [SDKS-2545]
- Deep link support for `mfauth` scheme in Authenticator sample app [SDKS-2524]
- Interface for access_token refresh [SDKS-2563]
- Ability to process new JSON format of IG policy advice [SDKS-2239]

#### Fixed
- Fixed an issue on parsing `issuer` from combined MFA registration uri [SDKS-2542]
- Added error message about duplicated accounts while performing combined MFA registration [SDKS-2627]

## [4.0.0]
#### Added
Expand Down
4 changes: 2 additions & 2 deletions FRAuth.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'FRAuth'
s.version = '4.0.0'
s.version = '4.1.0'
s.summary = 'ForgeRock Auth SDK for iOS'
s.description = <<-DESC
FRAuth is a SDK that allows you easily and quickly develop an application with ForgeRock Platform or ForgeRock Identity Cloud. FRAuth SDK provides interfaces and functionalities of user authentication, registration, and identity and access management against ForgeRock solutions.
Expand All @@ -29,5 +29,5 @@ Pod::Spec.new do |s|

base_dir = "FRAuth/FRAuth"
s.source_files = base_dir + '/**/*.swift', base_dir + '/**/*.c', base_dir + '/**/*.h'
s.ios.dependency 'FRCore', '~> 4.0.0'
s.ios.dependency 'FRCore', '~> 4.1.0'
end
21 changes: 19 additions & 2 deletions FRAuth/FRAuth.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
/* Begin PBXBuildFile section */
3A1B43D0284510B700EAFC9D /* AtomicDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1B43CF284510B700EAFC9D /* AtomicDictionary.swift */; };
3A1B43D3284524B900EAFC9D /* AtomicDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1B43D2284524B900EAFC9D /* AtomicDictionaryTests.swift */; };

3A53E4DA2A153AF200E17DDF /* PolicyAdviceCreatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A53E4D92A153AF200E17DDF /* PolicyAdviceCreatorTests.swift */; };
3A6D26672A1345400099D877 /* PolicyAdviceCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A6D26662A1345400099D877 /* PolicyAdviceCreator.swift */; };

959D7D98290B4B9200A1F22F /* AA-05-DeviceBindingCallbackTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 959D7D97290B4B9200A1F22F /* AA-05-DeviceBindingCallbackTest.swift */; };
95E180B42992A6F20087457D /* AA-06-DeviceSigningVerifierCallbackTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E180B32992A6F20087457D /* AA-06-DeviceSigningVerifierCallbackTest.swift */; };

A5950A2A27EA205B00EDEFE4 /* SSLPinningTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5950A2927EA205B00EDEFE4 /* SSLPinningTests.swift */; };
D512CD41240DC41E00AF520E /* FRRestClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D512CD40240DC41E00AF520E /* FRRestClient.swift */; };
D513F17524DA6B490042228B /* AuthApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D513F17424DA6B490042228B /* AuthApiError.swift */; };
Expand Down Expand Up @@ -323,8 +328,13 @@
/* Begin PBXFileReference section */
3A1B43CF284510B700EAFC9D /* AtomicDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicDictionary.swift; sourceTree = "<group>"; };
3A1B43D2284524B900EAFC9D /* AtomicDictionaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicDictionaryTests.swift; sourceTree = "<group>"; };

3A53E4D92A153AF200E17DDF /* PolicyAdviceCreatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolicyAdviceCreatorTests.swift; sourceTree = "<group>"; };
3A6D26662A1345400099D877 /* PolicyAdviceCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolicyAdviceCreator.swift; sourceTree = "<group>"; };

959D7D97290B4B9200A1F22F /* AA-05-DeviceBindingCallbackTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AA-05-DeviceBindingCallbackTest.swift"; sourceTree = "<group>"; };
95E180B32992A6F20087457D /* AA-06-DeviceSigningVerifierCallbackTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AA-06-DeviceSigningVerifierCallbackTest.swift"; sourceTree = "<group>"; };

A5950A2927EA205B00EDEFE4 /* SSLPinningTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSLPinningTests.swift; sourceTree = "<group>"; };
D5015FEE24996AE50025FEB6 /* MetadataCallbackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetadataCallbackTests.swift; sourceTree = "<group>"; };
D5054B4D244680C3007FBA92 /* DeviceProfileCallbackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceProfileCallbackTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -951,6 +961,7 @@
isa = PBXGroup;
children = (
D5723C3E23F4C66A00557AA8 /* PolicyAdvice.swift */,
3A6D26662A1345400099D877 /* PolicyAdviceCreator.swift */,
);
path = Authorization;
sourceTree = "<group>";
Expand Down Expand Up @@ -1433,6 +1444,7 @@
isa = PBXGroup;
children = (
D5DE4BB02488AFD000FE2654 /* PolicyAdviceTests.swift */,
3A53E4D92A153AF200E17DDF /* PolicyAdviceCreatorTests.swift */,
);
path = Authorization;
sourceTree = "<group>";
Expand Down Expand Up @@ -1697,6 +1709,7 @@
D586CFA223358EE0007A2194 /* StringAttributeInputCallback.swift in Sources */,
D53A8041262789BD0093B1CA /* PlatformAuthenticatorMakeCredentialSession.swift in Sources */,
D53A804E262789BD0093B1CA /* WAKTypes.swift in Sources */,
3A6D26672A1345400099D877 /* PolicyAdviceCreator.swift in Sources */,
D53A804A262789BD0093B1CA /* AuthenticatorData.swift in Sources */,
D53A803E262789BD0093B1CA /* Bytes.swift in Sources */,
D53A8044262789BD0093B1CA /* PlatformAuthenticatorConfig.swift in Sources */,
Expand Down Expand Up @@ -1884,7 +1897,11 @@
D5791BDD25F87DE8004B487A /* TokenTests.swift in Sources */,
D5791BDE25F87DE8004B487A /* PKCETests.swift in Sources */,
D58BC39F2602FB7700254654 /* IdPValueTests.swift in Sources */,

3A53E4DA2A153AF200E17DDF /* PolicyAdviceCreatorTests.swift in Sources */,

959D7D98290B4B9200A1F22F /* AA-05-DeviceBindingCallbackTest.swift in Sources */,

ECDF5F4C296851DD007BB721 /* FRWebAuthnTests.swift in Sources */,
D5791BDF25F87DE8004B487A /* AccessTokenTests.swift in Sources */,
D5791BE025F87DE8004B487A /* FRUserTests.swift in Sources */,
Expand Down Expand Up @@ -2058,7 +2075,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = "4.0.0";
MARKETING_VERSION = 4.1.0;
MODULEMAP_FILE = "";
OTHER_CFLAGS = "-DXCODE_FRAMEWORK=1";
PRODUCT_BUNDLE_IDENTIFIER = com.forgerock.ios.FRAuth;
Expand Down Expand Up @@ -2095,7 +2112,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = "4.0.0";
MARKETING_VERSION = 4.1.0;
MODULEMAP_FILE = "";
OTHER_CFLAGS = "-DXCODE_FRAMEWORK=1";
PRODUCT_BUNDLE_IDENTIFIER = com.forgerock.ios.FRAuth;
Expand Down
21 changes: 13 additions & 8 deletions FRAuth/FRAuth/Authorization/PolicyAdvice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// PolicyAdvice.swift
// FRAuth
//
// Copyright (c) 2020 ForgeRock. All rights reserved.
// Copyright (c) 2020 - 2023 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 Down Expand Up @@ -47,7 +47,7 @@ public enum AdviceType: String {
guard let url = URL(string: redirectUrl), let xmlstring = url.valueOf("authIndexValue"), let authIndexType = url.valueOf("authIndexType") else {
return nil
}

self.authIndexType = authIndexType
self.authIndexValue = xmlstring

Expand Down Expand Up @@ -91,12 +91,15 @@ public enum AdviceType: String {

if let advicesJSON = json["advices"] as? [String: Any], advicesJSON.keys.count > 0 {
advices = advicesJSON
} else {
advices = json
}


if let advices = advices, let adviceKey = advices.keys.first, let adviceValues = advices[adviceKey] as? [String], let adviceValue = adviceValues.first, let adviceType = AdviceType(rawValue: adviceKey) {

authIndexType = OpenAM.compositeAdvice
authIndexValue = "<Advices><AttributeValuePair><Attribute name=\"\(adviceType.rawValue)\"/><Value>\(adviceValue)</Value></AttributeValuePair></Advices>"
self.authIndexType = OpenAM.compositeAdvice
self.authIndexValue = "<Advices><AttributeValuePair><Attribute name=\"\(adviceType.rawValue)\"/><Value>\(adviceValue)</Value></AttributeValuePair></Advices>"
type = adviceType
value = adviceValue

Expand All @@ -109,7 +112,6 @@ public enum AdviceType: String {
}
}


/** Initializes PolicyAdvice object with authorization policy type, and value.
With example JSON payload shown below, 'TransactionConditionAdvice' is type of PolicyAdvice, and '9dae2c80-fe7a-4a36-b57b-4fb1271b0687' is value of PolicyAdvice
````
Expand All @@ -128,15 +130,18 @@ public enum AdviceType: String {
- Parameter type: Type of authorization policy in string; 'TransactionConditionAdvice' or 'AuthenticateToServiceConditionAdvice'
- Parameter value: String value of authorization policy; (i.e. transactionId, or Authentication Tree name)
**/
@objc public init?(type: String, value: String) {
@objc public init?(type: String,
value: String,
authIndexType: String? = nil,
authIndexValue: String? = nil) {

guard let adviceType = AdviceType(rawValue: type) else {
FRLog.w("Failed to parse AdviceType string value")
return nil
}

authIndexType = OpenAM.compositeAdvice
authIndexValue = "<Advices><AttributeValuePair><Attribute name=\"\(adviceType.rawValue)\"/><Value>\(value)</Value></AttributeValuePair></Advices>"
self.authIndexType = authIndexType ?? OpenAM.compositeAdvice
self.authIndexValue = authIndexValue ?? "<Advices><AttributeValuePair><Attribute name=\"\(adviceType.rawValue)\"/><Value>\(value)</Value></AttributeValuePair></Advices>"
self.type = adviceType
self.value = value

Expand Down
80 changes: 80 additions & 0 deletions FRAuth/FRAuth/Authorization/PolicyAdviceCreator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// PolicyAdviceCreator.swift
// FRAuth
//
// Copyright (c) 2023 ForgeRock. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
//


import Foundation

/// PolicyAdviceCreator helps create a Authorization PolicyAdvice based on different response types (xml, base64XML, json) that receive from AM's policy engine
class PolicyAdviceCreator {

private let adviceKey = "advices"
private let valueKey = "authIndexValue"
private let typeKey = "authIndexType"

/// Parse the advice json
/// - Parameters:
/// - advice: The Advice in key value form
/// - Returns: The parsed PolicyAdvice
func parseAsBase64(advice: String) -> PolicyAdvice? {
var dict: [String: String] = [:]
let regex = try? NSRegularExpression(pattern: "^\"|\"$", options: [])
advice.components(separatedBy: ",").forEach { value in
let componenets = value.components(separatedBy: "=")
if(componenets.count >= 2 ) {
dict[componenets[0]] = regex?.stringByReplacingMatches(in: componenets[1], range: NSMakeRange(0, componenets[1].count), withTemplate: "")
}
}
guard let advices = dict[adviceKey], let decodedAdvices = advices.decodeURL(),
let adviceDict = try? JSONSerialization.jsonObject(with: decodedAdvices, options: []) as? [String: Any] else {
return nil
}
return PolicyAdvice(json: adviceDict)
}

/// Parse the advice json
/// - Parameters:
/// - advice: The Advice in XML or base64 encoded form
/// - Returns: The parsed PolicyAdvice
func parse(advice: String) -> PolicyAdvice? {
guard let urlString = URL(string: advice), let authIndexValue = urlString.valueOf(valueKey), let authIndexType = urlString.valueOf(typeKey) else {
return nil
}

// Try Parse the XML.
guard let result = parseXML(advice: authIndexValue) else {
//On Failure Decode and parse the XML
return decodeAndParseXML(authIndexType: authIndexType,
authIndexValue: authIndexValue)
}
return PolicyAdvice(type: result.0,
value: result.1,
authIndexType: authIndexType,
authIndexValue: authIndexValue)
}

private func decodeAndParseXML(authIndexType: String,
authIndexValue: String) -> PolicyAdvice? {
guard let data = authIndexValue.decodeURL(),
let decodeXML = String(data: data, encoding: .utf8),
let result = parseXML(advice: decodeXML) else { return nil }

return PolicyAdvice(type: result.0,
value: result.1,
authIndexType: authIndexType,
authIndexValue: decodeXML)
}

private func parseXML(advice: String) -> (String, String)? {
guard let valueRange = advice.range(of: #"(?<=\<Value\>).*?(?=\<\/Value\>)"#, options: .regularExpression), let nameRange = advice.range(of: #"(?<=\<Attribute name=\").*?(?=\")"#, options: .regularExpression) else {
return nil
}
return (String(advice[nameRange]), String(advice[valueRange]))
}
}
10 changes: 7 additions & 3 deletions FRAuth/FRAuth/Config/OAuth2Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// OAuth2Client.swift
// FRAuth
//
// Copyright (c) 2019-2022 ForgeRock. All rights reserved.
// Copyright (c) 2019-2023 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 Down Expand Up @@ -56,12 +56,16 @@ public class OAuth2Client: NSObject, Codable {
///
/// - Parameters:
/// - accessToken: AccessToken object to revoke
/// - useRefreshToken: Whether to use refreshToken or not. `true` by default
/// - completion: Completion callback to notify the result of operation
@objc
public func revoke(accessToken: AccessToken, completion: @escaping CompletionCallback) {
public func revoke(accessToken: AccessToken, useRefreshToken: Bool = true, completion: @escaping CompletionCallback) {
// Construct parameter for the request
var parameter:[String: String] = [:]
let token = accessToken.refreshToken ?? accessToken.value
var token = accessToken.value
if useRefreshToken {
token = accessToken.refreshToken ?? accessToken.value
}
parameter[OAuth2.token] = token
parameter[OAuth2.clientId] = self.clientId

Expand Down
10 changes: 9 additions & 1 deletion FRAuth/FRAuth/Manager/TokenManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// TokenManager.swift
// FRAuth
//
// Copyright (c) 2019-2022 ForgeRock. All rights reserved.
// Copyright (c) 2019-2023 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 Down Expand Up @@ -365,4 +365,12 @@ struct TokenManager {
FRUser._staticUser = nil
Browser.currentBrowser = nil
}


/// Revoke given Access Token without using the Refresh Token
func revokeToken(_ token: AccessToken, completion: @escaping CompletionCallback) {
self.oAuth2Client.revoke(accessToken: token, useRefreshToken: false) { (error) in
completion(error)
}
}
}
11 changes: 9 additions & 2 deletions FRAuth/FRAuth/Policy/AuthorizationPolicy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// AuthorizationPolicy.swift
// FRAuth
//
// Copyright (c) 2020 ForgeRock. All rights reserved.
// Copyright (c) 2020 - 2023 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 Down Expand Up @@ -101,7 +101,7 @@ import Foundation
/// - Returns: PolicyAdvice if given redirect response matches with any of conditions
func evaluateAuthorizationPolicyWithRedirect(responseData: Data?, session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest) -> PolicyAdvice? {
FRLog.v("[AuthorizationPolicy] Evaluating Authorization Policy started")
if response.statusCode == 307, let redirectUrl = response.allHeaderFields["Location"] as? String, let policyAdvise = PolicyAdvice(redirectUrl: redirectUrl) {
if response.statusCode == 307, let redirectUrl = response.allHeaderFields["Location"] as? String, let policyAdvise = PolicyAdviceCreator().parse(advice: redirectUrl) {
FRLog.i("[AuthorizationPolicy] IG request redirect (307) for Authorization Policy found; constructed PolicyAdvice based on IG redirection")
return policyAdvise
}
Expand Down Expand Up @@ -131,6 +131,13 @@ import Foundation
/// - Returns: PolicyAdvice if given redirect response matches with any of conditions
func evaluateAuthorizationPolicy(responseData: Data?, response: URLResponse?, error: Error?) -> PolicyAdvice? {
FRLog.v("[AuthorizationPolicy] Evaluating Authorization Policy started")

if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 401, let json = httpResponse.allHeaderFields["Www-Authenticate"] as? String,
let policyAdvice = PolicyAdviceCreator().parseAsBase64(advice: json) {
FRLog.i("[AuthorizationPolicy] PolicyAdvice JSON object found from response JSON payload; returning PolicyAdvice")
return policyAdvice
}

if let data = responseData, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]], let evalResult = json.first, let policyAdvice = PolicyAdvice(json: evalResult) {
FRLog.i("[AuthorizationPolicy] PolicyAdvice JSON object found from response JSON payload; returning PolicyAdvice")
return policyAdvice
Expand Down
Loading

0 comments on commit f797c8c

Please sign in to comment.