Skip to content

Commit 6b67b66

Browse files
authored
fix(Auth): Refactor the expires in logic of the authentication result (#3114)
1 parent e3a7665 commit 6b67b66

File tree

5 files changed

+106
-7
lines changed

5 files changed

+106
-7
lines changed

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/RefreshAuthorizationSession/UserPool/RefreshHostedUITokens.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,12 @@ struct RefreshHostedUITokens: Action {
9797
throw FetchSessionError.service(error)
9898

9999
} else if let idToken = json["id_token"] as? String,
100-
let accessToken = json["access_token"] as? String,
101-
let expiresIn = json["expires_in"] as? Int {
100+
let accessToken = json["access_token"] as? String {
102101
let userPoolTokens = AWSCognitoUserPoolTokens(
103102
idToken: idToken,
104103
accessToken: accessToken,
105104
refreshToken: existingSignedIndata.cognitoUserPoolTokens.refreshToken,
106-
expiresIn: expiresIn)
105+
expiresIn: json["expires_in"] as? Int)
107106
return SignedInData(
108107
signedInDate: existingSignedIndata.signedInDate,
109108
signInMethod: existingSignedIndata.signInMethod,

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Actions/SignIn/HostedUI/FetchHostedUISignInToken.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,12 @@ struct FetchHostedUISignInToken: Action {
8181

8282
} else if let idToken = json["id_token"] as? String,
8383
let accessToken = json["access_token"] as? String,
84-
let refreshToken = json["refresh_token"] as? String,
85-
let expiresIn = json["expires_in"] as? Int {
84+
let refreshToken = json["refresh_token"] as? String {
8685
let userPoolTokens = AWSCognitoUserPoolTokens(
8786
idToken: idToken,
8887
accessToken: accessToken,
8988
refreshToken: refreshToken,
90-
expiresIn: expiresIn)
89+
expiresIn: json["expires_in"] as? Int)
9190
let signedInData = SignedInData(
9291
signedInDate: Date(),
9392
signInMethod: .hostedUI(result.options),

AmplifyPlugins/Auth/Sources/AWSCognitoAuthPlugin/Models/AWSCognitoUserPoolTokens.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ public struct AWSCognitoUserPoolTokens: AuthCognitoTokens {
1616

1717
public let refreshToken: String
1818

19+
@available(*, deprecated, message: "Use of `expiration` is deprecated, use `exp` claim in the `idToken` or `accessToken` for expiries")
1920
public let expiration: Date
2021

22+
@available(*, deprecated, message: "Use of `init(idToken,accessToken,refreshToken:expiresIn)` is deprecated, use `exp` claim in the `idToken` or `accessToken` instead")
2123
public init(idToken: String,
2224
accessToken: String,
2325
refreshToken: String,
@@ -28,6 +30,7 @@ public struct AWSCognitoUserPoolTokens: AuthCognitoTokens {
2830
self.expiration = Date().addingTimeInterval(TimeInterval(expiresIn))
2931
}
3032

33+
@available(*, deprecated, message: "Use of `init(idToken,accessToken,refreshToken:expiration)` is deprecated, use `exp` claim in the `idToken` or `accessToken` instead")
3134
public init(idToken: String,
3235
accessToken: String,
3336
refreshToken: String,
@@ -38,6 +41,37 @@ public struct AWSCognitoUserPoolTokens: AuthCognitoTokens {
3841
self.expiration = expiration
3942
}
4043

44+
init(idToken: String,
45+
accessToken: String,
46+
refreshToken: String,
47+
expiresIn: Int? = nil) {
48+
49+
self.idToken = idToken
50+
self.accessToken = accessToken
51+
self.refreshToken = refreshToken
52+
53+
if let expiresIn = expiresIn {
54+
self.expiration = Date().addingTimeInterval(TimeInterval(expiresIn))
55+
} else {
56+
let expirationDoubleValue: Double
57+
let idTokenExpiration = try? AWSAuthService().getTokenClaims(tokenString: idToken).get()["exp"]?.doubleValue
58+
let accessTokenExpiration = try? AWSAuthService().getTokenClaims(tokenString: accessToken).get()["exp"]?.doubleValue
59+
60+
switch (idTokenExpiration, accessTokenExpiration) {
61+
case (.some(let idTokenValue), .some(let accessTokenValue)):
62+
expirationDoubleValue = min(idTokenValue, accessTokenValue)
63+
case (.none, .some(let accessTokenValue)):
64+
expirationDoubleValue = accessTokenValue
65+
case (.some(let idTokenValue), .none):
66+
expirationDoubleValue = idTokenValue
67+
case (.none, .none):
68+
expirationDoubleValue = 0
69+
}
70+
71+
self.expiration = Date().addingTimeInterval(TimeInterval((expirationDoubleValue ?? 0)))
72+
}
73+
}
74+
4175
}
4276

4377
extension AWSCognitoUserPoolTokens: Equatable { }

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/Mocks/MockData/AWSCognitoUserPoolTokens+Mock.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//
77

88
import Foundation
9-
import AWSCognitoAuthPlugin
9+
@testable import AWSCognitoAuthPlugin
1010

1111
extension AWSCognitoUserPoolTokens {
1212

@@ -40,4 +40,17 @@ extension AWSCognitoUserPoolTokens {
4040
refreshToken: "refreshToken",
4141
expiresIn: 121)
4242
}
43+
44+
static var testDataWithoutExp: AWSCognitoUserPoolTokens {
45+
let tokenDataWithoutExp = [
46+
"sub": "1234567890",
47+
"username": "John Doe",
48+
"iat": "1516239022"
49+
]
50+
return AWSCognitoUserPoolTokens(
51+
idToken: CognitoAuthTestHelper.buildToken(for: tokenDataWithoutExp),
52+
accessToken: CognitoAuthTestHelper.buildToken(for: tokenDataWithoutExp),
53+
refreshToken: "refreshToken",
54+
expiresIn: nil)
55+
}
4356
}

AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/TaskTests/HostedUITests/AWSAuthHostedUISignInTests.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,60 @@ class AWSAuthHostedUISignInTests: XCTestCase {
8888
XCTAssertTrue(result.isSignedIn)
8989
}
9090

91+
@MainActor
92+
func testSuccessfulSignIn_missingExpiresIn() async throws {
93+
mockTokenResult = ["id_token": AWSCognitoUserPoolTokens.testData.idToken,
94+
"access_token": AWSCognitoUserPoolTokens.testData.accessToken,
95+
"refresh_token": AWSCognitoUserPoolTokens.testData.refreshToken] as [String: Any]
96+
mockJson = try! JSONSerialization.data(withJSONObject: mockTokenResult)
97+
MockURLProtocol.requestHandler = { _ in
98+
return (HTTPURLResponse(), self.mockJson)
99+
}
100+
101+
mockHostedUIResult = .success([
102+
.init(name: "state", value: mockState),
103+
.init(name: "code", value: mockProof)
104+
])
105+
let result = try await plugin.signInWithWebUI(presentationAnchor: ASPresentationAnchor(), options: nil)
106+
XCTAssertTrue(result.isSignedIn)
107+
}
108+
109+
@MainActor
110+
func testSuccessfulSignIn_missingExpiresIn_testTokenMissingExp() async throws {
111+
mockTokenResult = ["id_token": AWSCognitoUserPoolTokens.testDataWithoutExp.idToken,
112+
"access_token": AWSCognitoUserPoolTokens.testData.accessToken,
113+
"refresh_token": AWSCognitoUserPoolTokens.testData.refreshToken] as [String: Any]
114+
mockJson = try! JSONSerialization.data(withJSONObject: mockTokenResult)
115+
MockURLProtocol.requestHandler = { _ in
116+
return (HTTPURLResponse(), self.mockJson)
117+
}
118+
119+
mockHostedUIResult = .success([
120+
.init(name: "state", value: mockState),
121+
.init(name: "code", value: mockProof)
122+
])
123+
let result = try await plugin.signInWithWebUI(presentationAnchor: ASPresentationAnchor(), options: nil)
124+
XCTAssertTrue(result.isSignedIn)
125+
}
126+
127+
@MainActor
128+
func testSuccessfulSignIn_missingExpiresIn_testBothTokenMissingExp() async throws {
129+
mockTokenResult = ["id_token": AWSCognitoUserPoolTokens.testDataWithoutExp.idToken,
130+
"access_token": AWSCognitoUserPoolTokens.testDataWithoutExp.accessToken,
131+
"refresh_token": AWSCognitoUserPoolTokens.testData.refreshToken] as [String: Any]
132+
mockJson = try! JSONSerialization.data(withJSONObject: mockTokenResult)
133+
MockURLProtocol.requestHandler = { _ in
134+
return (HTTPURLResponse(), self.mockJson)
135+
}
136+
137+
mockHostedUIResult = .success([
138+
.init(name: "state", value: mockState),
139+
.init(name: "code", value: mockProof)
140+
])
141+
let result = try await plugin.signInWithWebUI(presentationAnchor: ASPresentationAnchor(), options: nil)
142+
XCTAssertTrue(result.isSignedIn)
143+
}
144+
91145
@MainActor
92146
func testUserCancelSignIn() async {
93147
mockHostedUIResult = .failure(.cancelled)

0 commit comments

Comments
 (0)