From 07f339194ac78b84f87173abd35ae52c85b1a9e9 Mon Sep 17 00:00:00 2001 From: Naufal Fachrian Date: Sun, 23 Jul 2023 17:07:55 +0700 Subject: [PATCH 1/4] Add option to use offline access_type for Google --- Sources/ImperialGoogle/Standard/Google.swift | 2 +- .../Standard/GoogleAccessType.swift | 4 +++ .../Standard/GoogleOffline.swift | 25 +++++++++++++++++++ .../Standard/GoogleRouter.swift | 17 ++++++++++++- 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 Sources/ImperialGoogle/Standard/GoogleAccessType.swift create mode 100644 Sources/ImperialGoogle/Standard/GoogleOffline.swift diff --git a/Sources/ImperialGoogle/Standard/Google.swift b/Sources/ImperialGoogle/Standard/Google.swift index 1589f3a5..ac5b3811 100644 --- a/Sources/ImperialGoogle/Standard/Google.swift +++ b/Sources/ImperialGoogle/Standard/Google.swift @@ -14,7 +14,7 @@ public class Google: FederatedService { scope: [String] = [], completion: @escaping (Request, String) throws -> (EventLoopFuture) ) throws { - self.router = try GoogleRouter(callback: callback, completion: completion) + self.router = try GoogleRouter(callback: callback, completion: completion, accessType: .online) self.tokens = self.router.tokens self.router.scope = scope diff --git a/Sources/ImperialGoogle/Standard/GoogleAccessType.swift b/Sources/ImperialGoogle/Standard/GoogleAccessType.swift new file mode 100644 index 00000000..03d808c0 --- /dev/null +++ b/Sources/ImperialGoogle/Standard/GoogleAccessType.swift @@ -0,0 +1,4 @@ +public enum GoogleAccessType: String { + case offline = "offline" + case online = "online" +} diff --git a/Sources/ImperialGoogle/Standard/GoogleOffline.swift b/Sources/ImperialGoogle/Standard/GoogleOffline.swift new file mode 100644 index 00000000..21d43333 --- /dev/null +++ b/Sources/ImperialGoogle/Standard/GoogleOffline.swift @@ -0,0 +1,25 @@ +@_exported import ImperialCore +import Vapor + +public class GoogleOffline: FederatedService { + public var tokens: FederatedServiceTokens + public var router: FederatedServiceRouter + + @discardableResult + public required init( + routes: RoutesBuilder, + authenticate: String, + authenticateCallback: ((Request) throws -> (EventLoopFuture))?, + callback: String, + scope: [String] = [], + completion: @escaping (Request, String) throws -> (EventLoopFuture) + ) throws { + self.router = try GoogleRouter(callback: callback, completion: completion, accessType: .offline) + self.tokens = self.router.tokens + + self.router.scope = scope + try self.router.configureRoutes(withAuthURL: authenticate, authenticateCallback: authenticateCallback, on: routes) + + OAuthService.register(.google) + } +} diff --git a/Sources/ImperialGoogle/Standard/GoogleRouter.swift b/Sources/ImperialGoogle/Standard/GoogleRouter.swift index 56b70519..5bc1ce7d 100644 --- a/Sources/ImperialGoogle/Standard/GoogleRouter.swift +++ b/Sources/ImperialGoogle/Standard/GoogleRouter.swift @@ -8,6 +8,7 @@ public class GoogleRouter: FederatedServiceRouter { public let callbackURL: String public let accessTokenURL: String = "https://www.googleapis.com/oauth2/v4/token" public let service: OAuthService = .google + public var accessType: GoogleAccessType = .online public let callbackHeaders: HTTPHeaders = { var headers = HTTPHeaders() headers.contentType = .urlEncodedForm @@ -19,6 +20,15 @@ public class GoogleRouter: FederatedServiceRouter { self.callbackURL = callback self.callbackCompletion = completion } + + public convenience init( + callback: String, + completion: @escaping (Request, String) throws -> (EventLoopFuture), + accessType: GoogleAccessType + ) throws { + try self.init(callback: callback, completion: completion) + self.accessType = accessType + } public func authURL(_ request: Request) throws -> String { var components = URLComponents() @@ -29,7 +39,8 @@ public class GoogleRouter: FederatedServiceRouter { clientIDItem, redirectURIItem, scopeItem, - codeResponseTypeItem + codeResponseTypeItem, + accessTypeItem ] guard let url = components.url else { @@ -46,4 +57,8 @@ public class GoogleRouter: FederatedServiceRouter { redirectURI: callbackURL) } + public var accessTypeItem: URLQueryItem { + .init(name: "access_type", value: accessType.rawValue) + } + } From 90bb299a210513f1cc350f5a5ed85f798326be1a Mon Sep 17 00:00:00 2001 From: Naufal Fachrian Date: Sun, 23 Jul 2023 18:10:31 +0700 Subject: [PATCH 2/4] Set refresh token after token received --- Sources/ImperialCore/Routing/FederatedServiceRouter.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/ImperialCore/Routing/FederatedServiceRouter.swift b/Sources/ImperialCore/Routing/FederatedServiceRouter.swift index 623a460a..de05090b 100644 --- a/Sources/ImperialCore/Routing/FederatedServiceRouter.swift +++ b/Sources/ImperialCore/Routing/FederatedServiceRouter.swift @@ -108,6 +108,9 @@ extension FederatedServiceRouter { .flatMap { buffer in return request.client.post(url, headers: self.callbackHeaders) { $0.body = buffer } }.flatMapThrowing { response in + if let refreshToken = try? response.content.get(String.self, at: ["refresh_token"]), !refreshToken.isEmpty { + request.session.setRefreshToken(refreshToken) + } return try response.content.get(String.self, at: ["access_token"]) } } From 07dab7d8db6a4f447617c8e5c981c2ea052641af Mon Sep 17 00:00:00 2001 From: Naufal Fachrian Date: Sun, 23 Jul 2023 18:43:16 +0700 Subject: [PATCH 3/4] Add prompt=consent if access_type is offline --- Sources/ImperialGoogle/Standard/GoogleRouter.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/ImperialGoogle/Standard/GoogleRouter.swift b/Sources/ImperialGoogle/Standard/GoogleRouter.swift index 5bc1ce7d..f5928b49 100644 --- a/Sources/ImperialGoogle/Standard/GoogleRouter.swift +++ b/Sources/ImperialGoogle/Standard/GoogleRouter.swift @@ -42,6 +42,10 @@ public class GoogleRouter: FederatedServiceRouter { codeResponseTypeItem, accessTypeItem ] + + if accessType == .offline { + components.queryItems?.append(promptItem) + } guard let url = components.url else { throw Abort(.internalServerError) @@ -61,4 +65,8 @@ public class GoogleRouter: FederatedServiceRouter { .init(name: "access_type", value: accessType.rawValue) } + public var promptItem: URLQueryItem { + .init(name: "prompt", value: "consent") + } + } From 8abe1601b32cafb2c430dc7b1030c38190a917e4 Mon Sep 17 00:00:00 2001 From: Naufal Fachrian Date: Sun, 23 Jul 2023 19:40:55 +0700 Subject: [PATCH 4/4] Update Google OAuth URL --- Sources/ImperialGoogle/Standard/GoogleRouter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ImperialGoogle/Standard/GoogleRouter.swift b/Sources/ImperialGoogle/Standard/GoogleRouter.swift index f5928b49..e84fe48c 100644 --- a/Sources/ImperialGoogle/Standard/GoogleRouter.swift +++ b/Sources/ImperialGoogle/Standard/GoogleRouter.swift @@ -34,7 +34,7 @@ public class GoogleRouter: FederatedServiceRouter { var components = URLComponents() components.scheme = "https" components.host = "accounts.google.com" - components.path = "/o/oauth2/auth" + components.path = "/o/oauth2/v2/auth" components.queryItems = [ clientIDItem, redirectURIItem,