diff --git a/Sources/Logto/Core/LogtoCore+Fetch.swift b/Sources/Logto/Core/LogtoCore+Fetch.swift index d9952fe..a71a206 100644 --- a/Sources/Logto/Core/LogtoCore+Fetch.swift +++ b/Sources/Logto/Core/LogtoCore+Fetch.swift @@ -46,7 +46,7 @@ public extension LogtoCore { struct CodeTokenResponse: Codable, Equatable { public let accessToken: String public let refreshToken: String? - public let idToken: String + public let idToken: String? public let scope: String public let expiresIn: Int64 } @@ -80,7 +80,7 @@ public extension LogtoCore { struct RefreshTokenTokenResponse: Codable, Equatable { public let accessToken: String - public let refreshToken: String + public let refreshToken: String? public let idToken: String? public let scope: String public let expiresIn: Int64 diff --git a/Sources/LogtoClient/LogtoClient/LogtoClient+AccessToken.swift b/Sources/LogtoClient/LogtoClient/LogtoClient+AccessToken.swift index 62e4f02..e56995e 100644 --- a/Sources/LogtoClient/LogtoClient/LogtoClient+AccessToken.swift +++ b/Sources/LogtoClient/LogtoClient/LogtoClient+AccessToken.swift @@ -34,7 +34,10 @@ extension LogtoClient { ) accessTokenMap[key] = accessToken - self.refreshToken = response.refreshToken + + response.refreshToken.map { + self.refreshToken = $0 + } response.idToken.map { self.idToken = $0 diff --git a/Sources/LogtoClient/LogtoClient/LogtoClient+SignIn.swift b/Sources/LogtoClient/LogtoClient/LogtoClient+SignIn.swift index 91640e8..57c37de 100644 --- a/Sources/LogtoClient/LogtoClient/LogtoClient+SignIn.swift +++ b/Sources/LogtoClient/LogtoClient/LogtoClient+SignIn.swift @@ -30,12 +30,14 @@ extension LogtoClient { let response = try await session.start() let jwks = try await fetchJwkSet() - try LogtoUtilities.verifyIdToken( - response.idToken, - issuer: oidcConfig.issuer, - clientId: logtoConfig.appId, - jwks: jwks - ) + try response.idToken.map { + try LogtoUtilities.verifyIdToken( + $0, + issuer: oidcConfig.issuer, + clientId: logtoConfig.appId, + jwks: jwks + ) + } idToken = response.idToken refreshToken = response.refreshToken diff --git a/Tests/LogtoClientTests/LogtoClient/LogtoClientTests+AccessToken.swift b/Tests/LogtoClientTests/LogtoClient/LogtoClientTests+AccessToken.swift index 543fba1..cff1b55 100644 --- a/Tests/LogtoClientTests/LogtoClient/LogtoClientTests+AccessToken.swift +++ b/Tests/LogtoClientTests/LogtoClient/LogtoClientTests+AccessToken.swift @@ -35,6 +35,27 @@ extension LogtoClientTests { XCTAssertEqual(tokens[0], "123") XCTAssertEqual(tokens[1], "456") + XCTAssertEqual(client.refreshToken, "789") + XCTAssertEqual(client.idToken, "abc") + } + + func testGetAccessTokenByRefreshTokenWithoutRefreshAndIdTokenReturned() async throws { + NetworkSessionMock.shared.tokenRequestCount = 0 + + let client = buildClient(withOidcEndpoint: "/oidc_config:good:no_refresh") + client.idToken = "baz" + client.refreshToken = "bar" + client.accessTokenMap[client.buildAccessTokenKey(for: "resource1")] = AccessToken( + token: "foo", + scope: "", + expiresAt: Date().timeIntervalSince1970 - 1 + ) + + let token = try await client.getAccessToken(for: "resource1") + + XCTAssertEqual(token, "123") + XCTAssertEqual(client.refreshToken, "bar") + XCTAssertEqual(client.idToken, "baz") } func testGetAccessTokenUnalbeToFetchOidcConfig() async throws { diff --git a/Tests/LogtoMock/NetworkSessionMock.swift b/Tests/LogtoMock/NetworkSessionMock.swift index 2986c0d..0a89057 100644 --- a/Tests/LogtoMock/NetworkSessionMock.swift +++ b/Tests/LogtoMock/NetworkSessionMock.swift @@ -33,6 +33,18 @@ public class NetworkSessionMock: NetworkSession { "issuer": "http://localhost:443/oidc" } """.utf8), nil) + case "oidc_config:good:no_refresh": + return (Data(""" + { + "authorization_endpoint": "https://logto.dev/auth:good", + "token_endpoint": "https://logto.dev/token:good:no_refresh", + "end_session_endpoint": "https://logto.dev/end:good", + "revocation_endpoint": "https://logto.dev/revoke:good", + "userinfo_endpoint": "https://logto.dev/user", + "jwks_uri": "https://logto.dev/jwks:good", + "issuer": "http://localhost:443/oidc" + } + """.utf8), nil) case "oidc_config:bad": return (Data(""" { @@ -86,6 +98,15 @@ public class NetworkSessionMock: NetworkSession { "expires_in": 123 } """.utf8), nil) + case "token:good:no_refresh": + return (Data(""" + { + "access_token": "123", + "token_type": "jwt", + "scope": "", + "expires_in": 123 + } + """.utf8), nil) case "revoke:good": return (nil, nil) default: