amplify-swift/AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/AWSAuthCognitoSession.swift

191 lines
6.7 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Amplify
import AWSPluginsCore
import Foundation
public struct AWSAuthCognitoSession: AuthSession,
AuthAWSCredentialsProvider,
AuthCognitoTokensProvider,
AuthCognitoIdentityProvider {
/// Indicates whether the user is signedIn or not
public var isSignedIn: Bool
public var userSubResult: Result<String, AuthError> {
return getUserSub()
}
public let identityIdResult: Result<String, AuthError>
public let awsCredentialsResult: Result<AWSCredentials, AuthError>
public let userPoolTokensResult: Result<AuthCognitoTokens, AuthError>
init(isSignedIn: Bool,
identityIdResult: Result<String, AuthError>,
awsCredentialsResult: Result<AWSCredentials, AuthError>,
cognitoTokensResult: Result<AuthCognitoTokens, AuthError>) {
self.isSignedIn = isSignedIn
self.identityIdResult = identityIdResult
self.awsCredentialsResult = awsCredentialsResult
self.userPoolTokensResult = cognitoTokensResult
}
public func getAWSCredentials() -> Result<AWSCredentials, AuthError> {
return awsCredentialsResult
}
public func getCognitoTokens() -> Result<AuthCognitoTokens, AuthError> {
return userPoolTokensResult
}
public func getIdentityId() -> Result<String, AuthError> {
return identityIdResult
}
public func getUserSub() -> Result<String, AuthError> {
do {
let tokens = try userPoolTokensResult.get()
let claims = try AWSAuthService().getTokenClaims(tokenString: tokens.idToken).get()
guard let userSub = claims["sub"] as? String else {
let error = AuthError.unknown("""
Could not retreive user sub from the fetched Cognito tokens.
""")
return .failure(error)
}
return .success(userSub)
} catch AuthError.signedOut {
return .failure(AuthError.signedOut(
AuthPluginErrorConstants.userSubSignOutError.errorDescription,
AuthPluginErrorConstants.userSubSignOutError.recoverySuggestion))
} catch let error as AuthError {
return .failure(error)
} catch {
let error = AuthError.unknown("""
Could not retreive user sub from the fetched Cognito tokens.
""")
return .failure(error)
}
}
}
/// Internal Helpers for managing session tokens
internal extension AWSAuthCognitoSession {
func areTokensExpiring(in seconds: TimeInterval? = nil) -> Bool {
guard let tokens = try? userPoolTokensResult.get(),
let idTokenClaims = try? AWSAuthService().getTokenClaims(tokenString: tokens.idToken).get(),
let accessTokenClaims = try? AWSAuthService().getTokenClaims(tokenString: tokens.idToken).get(),
let idTokenExpiration = idTokenClaims["exp"]?.doubleValue,
let accessTokenExpiration = accessTokenClaims["exp"]?.doubleValue else {
return true
}
// If the session expires < X minutes return it
return (Date(timeIntervalSince1970: idTokenExpiration).compare(Date(timeIntervalSinceNow: seconds ?? 0)) == .orderedDescending &&
Date(timeIntervalSince1970: accessTokenExpiration).compare(Date(timeIntervalSinceNow: seconds ?? 0)) == .orderedDescending)
}
}
extension AWSAuthCognitoSession: Equatable {
public static func == (lhs: AWSAuthCognitoSession, rhs: AWSAuthCognitoSession) -> Bool {
switch (lhs.getCognitoTokens(), rhs.getCognitoTokens()) {
case (.success(let lhsTokens), .success(let rhsTokens)):
if (lhsTokens as? AWSCognitoUserPoolTokens) != (rhsTokens as? AWSCognitoUserPoolTokens) {
return false
}
case (.failure(let lhsError), .failure(let rhsError)):
if lhsError != rhsError {
return false
}
default:
return false
}
switch (lhs.getAWSCredentials(), rhs.getAWSCredentials()) {
case (.success(let lhsCredentials), .success(let rhsCredentials)):
if (lhsCredentials as? AuthAWSCognitoCredentials) != (rhsCredentials as? AuthAWSCognitoCredentials) {
return false
}
case (.failure(let lhsError), .failure(let rhsError)):
if lhsError != rhsError {
return false
}
default:
return false
}
switch (lhs.getIdentityId(), rhs.getIdentityId()) {
case (.success(let lhsIdentityId), .success(let rhsIdentityId)):
if lhsIdentityId != rhsIdentityId {
return false
}
case (.failure(let lhsError), .failure(let rhsError)):
if lhsError != rhsError {
return false
}
default:
return false
}
if lhs.isSignedIn != rhs.isSignedIn {
return false
}
return true
}
}
extension AWSAuthCognitoSession: CustomDebugDictionaryConvertible {
var debugDictionary: [String: Any] {
var dict = [
"isSignedIn": isSignedIn.description
]
switch identityIdResult {
case .success(let result):
dict["identityId"] = result.masked(interiorCount: 5, retainingCount: 5)
case .failure(let error):
dict["identityIdError"] = error.debugDescription
}
switch userSubResult {
case .success(let result):
dict["userSub"] = result.masked(interiorCount: 5, retainingCount: 5)
case .failure(let error):
dict["userSubError"] = error.debugDescription
}
switch userPoolTokensResult {
case .success(let result):
if let result = result as? AWSCognitoUserPoolTokens {
dict["cognitoTokens"] = result.debugDescription
}
case .failure(let error):
dict["cognitoTokensError"] = error.debugDescription
}
switch awsCredentialsResult {
case .success(let result):
if let result = result as? AuthAWSCognitoCredentials {
dict["AWS Credentials"] = result.debugDescription
}
case .failure(let error):
dict["awsCredentialsError"] = error.debugDescription
}
return dict
}
}
extension AWSAuthCognitoSession: CustomDebugStringConvertible {
public var debugDescription: String {
(debugDictionary as AnyObject).description
}
}