From c7e4713e3af74fe9d8e8bf372494ddfb2b96dd34 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Fri, 8 Jul 2022 12:22:12 -0400 Subject: [PATCH] fix: create valid unauthorized request for odic/userpool connections --- .../Interceptor/OIDCAuthInterceptor.swift | 9 +++++- .../OIDCAuthInterceptorTests.swift | 28 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/AppSyncRealTimeClient/Interceptor/OIDCAuthInterceptor.swift b/AppSyncRealTimeClient/Interceptor/OIDCAuthInterceptor.swift index 4968e5f9..e242b35b 100644 --- a/AppSyncRealTimeClient/Interceptor/OIDCAuthInterceptor.swift +++ b/AppSyncRealTimeClient/Interceptor/OIDCAuthInterceptor.swift @@ -52,7 +52,14 @@ public class OIDCAuthInterceptor: AuthInterceptor, AuthInterceptorAsync { case .success(let token): jwtToken = token case .failure: - return request + // A user that is not signed in should receive an unauthorized error from the connection attempt. This code + // achieves this by always creating a valid request to AppSync even when the token cannot be retrieved. The + // request sent to AppSync will receive a response indicating the request is unauthorized. If we do not use + // empty token string and perform the remaining logic of the request construction then it will fail request + // validation at AppSync before the authorization check, which ends up being propagated back to the caller + // as a "bad request". Example of bad requests are when the header and payload query strings are missing + // or when the data is not base64 encoded. + jwtToken = "" } let authHeader = UserPoolsAuthenticationHeader(token: jwtToken, host: host) diff --git a/AppSyncRealTimeClientTests/Interceptor/OIDCAuthInterceptorTests.swift b/AppSyncRealTimeClientTests/Interceptor/OIDCAuthInterceptorTests.swift index c1880b82..afa69ca5 100644 --- a/AppSyncRealTimeClientTests/Interceptor/OIDCAuthInterceptorTests.swift +++ b/AppSyncRealTimeClientTests/Interceptor/OIDCAuthInterceptorTests.swift @@ -10,13 +10,29 @@ import AppSyncRealTimeClient class OIDCAuthInterceptorTests: XCTestCase { + var userPoolAuthProvider: MockUserPoolsAuthProvider! var authInterceptor: OIDCAuthInterceptor! override func setUp() { - authInterceptor = OIDCAuthInterceptor(MockUserPoolsAuthProvider()) + userPoolAuthProvider = MockUserPoolsAuthProvider() + authInterceptor = OIDCAuthInterceptor(userPoolAuthProvider) } - func testInterceptRequest() { + func testInterceptConnection() { + let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")! + let request = AppSyncConnectionRequest(url: url) + let signedRequest = authInterceptor.interceptConnection(request, for: url) + + guard let queries = URLComponents(url: signedRequest.url, resolvingAgainstBaseURL: true)?.queryItems else { + assertionFailure("Query parameters should not be nil") + return + } + XCTAssertTrue(queries.contains { $0.name == "header"}, "Should contain the header query") + XCTAssertTrue(queries.contains { $0.name == "payload"}, "Should contain the payload query") + } + + func testInterceptConnectionWithInvalidToken() { + userPoolAuthProvider.hasError = true let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")! let request = AppSyncConnectionRequest(url: url) let signedRequest = authInterceptor.interceptConnection(request, for: url) @@ -39,7 +55,15 @@ class OIDCAuthInterceptorTests: XCTestCase { } class MockUserPoolsAuthProvider: OIDCAuthProvider { + struct AuthError: Error { } + + var hasError: Bool = false + func getLatestAuthToken() -> Result { + if hasError { + return .failure(AuthError()) + } + return .success("jwtToken") } }