diff --git a/Sources/ImperialAuth0/Auth0Router.swift b/Sources/ImperialAuth0/Auth0Router.swift index 3786091..5096d00 100644 --- a/Sources/ImperialAuth0/Auth0Router.swift +++ b/Sources/ImperialAuth0/Auth0Router.swift @@ -52,6 +52,7 @@ final public class Auth0Router: FederatedServiceRouter { clientId: self.tokens.clientID, clientSecret: self.tokens.clientSecret, code: code, - redirectURI: self.callbackURL) + redirectURI: self.callbackURL + ) } } diff --git a/Sources/ImperialDiscord/DiscordRouter.swift b/Sources/ImperialDiscord/DiscordRouter.swift index a44033c..06435ca 100644 --- a/Sources/ImperialDiscord/DiscordRouter.swift +++ b/Sources/ImperialDiscord/DiscordRouter.swift @@ -20,7 +20,6 @@ final public class DiscordRouter: FederatedServiceRouter { } public func authURL(_ request: Request) throws -> String { - var components = URLComponents() components.scheme = "https" components.host = "discord.com" diff --git a/Sources/ImperialDropbox/DropboxRouter.swift b/Sources/ImperialDropbox/DropboxRouter.swift index dd5d905..fe021ea 100644 --- a/Sources/ImperialDropbox/DropboxRouter.swift +++ b/Sources/ImperialDropbox/DropboxRouter.swift @@ -48,7 +48,8 @@ final public class DropboxRouter: FederatedServiceRouter { public func callbackBody(with code: String) -> any AsyncResponseEncodable { DropboxCallbackBody( code: code, - redirectURI: callbackURL) + redirectURI: callbackURL + ) } } diff --git a/Sources/ImperialFacebook/FacebookRouter.swift b/Sources/ImperialFacebook/FacebookRouter.swift index a7448ad..a0d79fd 100644 --- a/Sources/ImperialFacebook/FacebookRouter.swift +++ b/Sources/ImperialFacebook/FacebookRouter.swift @@ -42,7 +42,8 @@ final public class FacebookRouter: FederatedServiceRouter { code: code, clientId: tokens.clientID, clientSecret: tokens.clientSecret, - redirectURI: callbackURL) + redirectURI: callbackURL + ) } } diff --git a/Sources/ImperialGoogle/Standard/GoogleRouter.swift b/Sources/ImperialGoogle/Standard/GoogleRouter.swift index b4641ff..7707dc6 100644 --- a/Sources/ImperialGoogle/Standard/GoogleRouter.swift +++ b/Sources/ImperialGoogle/Standard/GoogleRouter.swift @@ -47,7 +47,8 @@ final public class GoogleRouter: FederatedServiceRouter { code: code, clientId: tokens.clientID, clientSecret: tokens.clientSecret, - redirectURI: callbackURL) + redirectURI: callbackURL + ) } } diff --git a/Sources/ImperialKeycloak/KeycloakRouter.swift b/Sources/ImperialKeycloak/KeycloakRouter.swift index 6eff574..e86f3b6 100644 --- a/Sources/ImperialKeycloak/KeycloakRouter.swift +++ b/Sources/ImperialKeycloak/KeycloakRouter.swift @@ -34,6 +34,7 @@ final public class KeycloakRouter: FederatedServiceRouter { code: code, clientId: tokens.clientID, clientSecret: tokens.clientSecret, - redirectURI: callbackURL) + redirectURI: callbackURL + ) } } diff --git a/Sources/ImperialMicrosoft/MicrosoftRouter.swift b/Sources/ImperialMicrosoft/MicrosoftRouter.swift index 14e1917..95f9866 100644 --- a/Sources/ImperialMicrosoft/MicrosoftRouter.swift +++ b/Sources/ImperialMicrosoft/MicrosoftRouter.swift @@ -51,7 +51,8 @@ final public class MicrosoftRouter: FederatedServiceRouter { clientId: tokens.clientID, clientSecret: tokens.clientSecret, redirectURI: callbackURL, - scope: scope.joined(separator: " ")) + scope: scope.joined(separator: " ") + ) } } diff --git a/Tests/ImperialTests/ImperialTests.swift b/Tests/ImperialTests/ImperialTests.swift index 0f7795c..a3f9e4d 100644 --- a/Tests/ImperialTests/ImperialTests.swift +++ b/Tests/ImperialTests/ImperialTests.swift @@ -25,9 +25,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // A real Auth0 domain is needed to test this route + #expect(res.status == .internalServerError) } ) } @@ -44,9 +45,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // Discord returns a 400 Bad Request error when the code is invalid with a JSON error message + #expect(res.status == .badRequest) } ) } @@ -63,9 +65,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // Dropbox returns a 400 Bad Request error when the code is invalid with a JSON error message + #expect(res.status == .badRequest) } ) } @@ -82,9 +85,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // The response is an JS, signaling an error with `redirect_uri` + #expect(res.status == .unsupportedMediaType) } ) } @@ -101,9 +105,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // The response is an HTML page likely signaling an error + #expect(res.status == .unsupportedMediaType) } ) } @@ -120,9 +125,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // Gitlab returns a 400 Bad Request error when the code is invalid with a JSON error message + #expect(res.status == .badRequest) } ) } @@ -139,9 +145,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // Google returns a 400 Bad Request error when the code is invalid with a JSON error message + #expect(res.status == .badRequest) } ) } @@ -160,7 +167,8 @@ struct ImperialTests { try await app.test( .GET, "/service-auth-complete", afterResponse: { res async throws in - #expect(res.status != .notFound) + // We don't have a valid key to sign the JWT + #expect(res.status == .internalServerError) } ) } @@ -177,9 +185,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // The post request fails + #expect(res.status == .internalServerError) } ) } @@ -196,9 +205,10 @@ struct ImperialTests { ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?code=123", afterResponse: { res async throws in - #expect(res.status != .notFound) + // Microsoft returns a 400 Bad Request, signaling an error with `redirect_uri` + #expect(res.status == .badRequest) } ) } diff --git a/Tests/ImperialTests/ShopifyTests.swift b/Tests/ImperialTests/ShopifyTests.swift index 804ca6d..a84e661 100644 --- a/Tests/ImperialTests/ShopifyTests.swift +++ b/Tests/ImperialTests/ShopifyTests.swift @@ -9,16 +9,22 @@ struct ShopifyTests { @Test("Shopify Route") func shopifyRoute() async throws { try await withApp(service: Shopify.self) { app in try await app.test( - .GET, "/service", + .GET, "/service?shop=some-shop.myshopify.com", afterResponse: { res async throws in - #expect(res.status != .notFound) + #expect(res.status == .seeOther) } ) try await app.test( - .GET, "/service-auth-complete", + .GET, "/service-auth-complete?" + + "code=0907a61c0c8d55e99db179b68161bc00&" + + "hmac=700e2dadb827fcc8609e9d5ce208b2e9cdaab9df07390d2cbca10d7c328fc4bf&" + + "shop=some-shop.myshopify.com&" + + "state=0.6784241404160823&" + + "timestamp=1337178173", afterResponse: { res async throws in - #expect(res.status != .notFound) + // The session should have the `nonce` property set + #expect(res.status == .badRequest) } ) } @@ -51,9 +57,12 @@ struct ShopifyTests { } @Test("HMAC Validation") func hmacValidation() throws { - let url = URL( - string: - "https://domain.com/?code=0907a61c0c8d55e99db179b68161bc00&hmac=700e2dadb827fcc8609e9d5ce208b2e9cdaab9df07390d2cbca10d7c328fc4bf&shop=some-shop.myshopify.com&state=0.6784241404160823×tamp=1337178173" + let url = URL(string: "https://domain.com/?" + + "code=0907a61c0c8d55e99db179b68161bc00&" + + "hmac=700e2dadb827fcc8609e9d5ce208b2e9cdaab9df07390d2cbca10d7c328fc4bf&" + + "shop=some-shop.myshopify.com&" + + "state=0.6784241404160823&" + + "timestamp=1337178173" )! let hmac = url.generateHMAC(key: "hush")