Skip to content

Commit

Permalink
Merge pull request #16 from vapor-community/develop
Browse files Browse the repository at this point in the history
Google Server - Server JWT
  • Loading branch information
calebkleveter authored Jun 6, 2018
2 parents b385dcb + a339dc1 commit 9b67da8
Show file tree
Hide file tree
Showing 109 changed files with 20,226 additions and 58 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/.build
/Packages
/*.xcodeproj
build
86 changes: 61 additions & 25 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,80 +6,98 @@
"repositoryURL": "https://github.com/vapor/console.git",
"state": {
"branch": null,
"revision": "273cf2ed1f4daba88a452494c342020171d8c7b8",
"version": "3.0.0-rc.2.2"
"revision": "5b9796d39f201b3dd06800437abd9d774a455e57",
"version": "3.0.2"
}
},
{
"package": "Core",
"repositoryURL": "https://github.com/vapor/core.git",
"state": {
"branch": null,
"revision": "225aff2b69049f2a4d9a4aae71d27ba7fb083032",
"version": "3.0.1"
"revision": "a909eccc41941faac6fb9e511cdb9a5cb30a05de",
"version": "3.1.7"
}
},
{
"package": "Crypto",
"repositoryURL": "https://github.com/vapor/crypto.git",
"state": {
"branch": null,
"revision": "4c7acb4fbf1720682275f0ef543474c951d84a09",
"version": "3.0.0-rc.3"
"revision": "1b8c2ba5a42f1adf2aa812204678d8b16466fa59",
"version": "3.1.2"
}
},
{
"package": "DatabaseKit",
"repositoryURL": "https://github.com/vapor/database-kit.git",
"state": {
"branch": null,
"revision": "9e77b5b95e3f1f141abdf75d6123f591971162cd",
"version": "1.0.0-rc.2.2.1"
"revision": "0db303439e5ef8b6df50a2b6c4029edddee90cb0",
"version": "1.0.1"
}
},
{
"package": "HTTP",
"repositoryURL": "https://github.com/vapor/http.git",
"state": {
"branch": null,
"revision": "5e766f72d81ef5fe8805d704efdffd17e4906134",
"version": "3.0.6"
}
},
{
"package": "Engine",
"repositoryURL": "https://github.com/vapor/engine.git",
"package": "JWT",
"repositoryURL": "https://github.com/vapor/jwt.git",
"state": {
"branch": null,
"revision": "4846ea9abeb04863f82f618ee8cb22650a82ae3c",
"revision": "6acca0abfe335ce7585d678cd721e8a0e3c7a053",
"version": "3.0.0-rc.2.1.1"
}
},
{
"package": "Multipart",
"repositoryURL": "https://github.com/vapor/multipart.git",
"state": {
"branch": null,
"revision": "7778dcb62f3efa845e8e2808937bb347575ba7ce",
"version": "3.0.1"
}
},
{
"package": "Routing",
"repositoryURL": "https://github.com/vapor/routing.git",
"state": {
"branch": null,
"revision": "2fc1d4de22a54848b35ad17b3e7f7816f19ebf90",
"version": "3.0.0-rc.2"
"revision": "3219e328491b0853b8554c5a694add344d2c6cfb",
"version": "3.0.1"
}
},
{
"package": "Service",
"repositoryURL": "https://github.com/vapor/service.git",
"state": {
"branch": null,
"revision": "02205a16a3f4714a08836529f5a868c2ce256719",
"version": "1.0.0-rc.2.2"
"revision": "281a70b69783891900be31a9e70051b6fe19e146",
"version": "1.0.0"
}
},
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "a0b7f646fba61402da384b3202f54c27debb5e13",
"version": "1.3.1"
"revision": "a5db2a67515ad2b490ac5646db204a5edf939f47",
"version": "1.6.1"
}
},
{
"package": "swift-nio-ssl",
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
"state": {
"branch": null,
"revision": "ea006b6368dbd9dbfd297deb6ddb3f070b72d043",
"version": "1.0.1"
"revision": "38955a5f806a952daf2b16fbfe9aa529749cf1dd",
"version": "1.1.0"
}
},
{
Expand All @@ -105,26 +123,44 @@
"repositoryURL": "https://github.com/vapor/template-kit.git",
"state": {
"branch": null,
"revision": "61ed3d3b0df4c181d6b88e06b418f425978ac2af",
"version": "1.0.0-rc.2.0.3"
"revision": "43b57b5861d5181b906ac6411d28645e980bb638",
"version": "1.0.1"
}
},
{
"package": "URLEncodedForm",
"repositoryURL": "https://github.com/vapor/url-encoded-form.git",
"state": {
"branch": null,
"revision": "57cf7fb9c1a1014c50bc05123684a9139ad44127",
"version": "1.0.3"
}
},
{
"package": "Validation",
"repositoryURL": "https://github.com/vapor/validation.git",
"state": {
"branch": null,
"revision": "1ce87fc2d18a8f15a491805825063c8db493a51e",
"version": "2.0.0-rc.2.1.1"
"revision": "ab6c5a352d97c8687b91ed4963aef8e7cfe0795b",
"version": "2.0.0"
}
},
{
"package": "Vapor",
"repositoryURL": "https://github.com/vapor/vapor.git",
"state": {
"branch": null,
"revision": "5e59691ef575597aa640da76ccb9fc5ee638f2c1",
"version": "3.0.0-rc.2.2.1"
"revision": "39b4d3fa36e58c6f7415c9da6c65a703bec34cea",
"version": "3.0.3"
}
},
{
"package": "WebSocket",
"repositoryURL": "https://github.com/vapor/websocket.git",
"state": {
"branch": null,
"revision": "141cb4d3814dc8062cb0b2f43e72801b5dfcf272",
"version": "1.0.1"
}
}
]
Expand Down
5 changes: 3 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ let package = Package(
.library(name: "Imperial", targets: ["Imperial"]),
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0-rc")
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
.package(url: "https://github.com/vapor/jwt.git", from: "3.0.0-rc")
],
targets: [
.target(name: "Imperial", dependencies: ["Vapor"]),
.target(name: "Imperial", dependencies: ["Vapor", "JWT"]),
.testTarget(name: "ImperialTests", dependencies: ["Imperial"]),
]
)
13 changes: 0 additions & 13 deletions Sources/Imperial/Helpers/Optional+Imperial.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,3 @@ extension Optional {
}
}
}

infix operator ??

/// Unwrappes an optional and returns the value or throws an error if `nil`.
///
/// - Parameters:
/// - lhs: The optional to unwrap.
/// - rhs: The error to throw if the optional is `nil`.
/// - Returns: The value that was contained in the optional.
/// - Throws: The error passed in if the optional is `nil`.
internal func ??<T>(lhs: T?, rhs: Error)throws -> T {
return try lhs.value(or: rhs)
}
2 changes: 1 addition & 1 deletion Sources/Imperial/Helpers/Request+Imperial.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension Request {
/// - Returns: An instance of the type passed in.
/// - Throws: Errors from trying to get the access token from the request.
func create<Model: FederatedCreatable>(_ model: Model.Type, with service: OAuthService)throws -> Future<Model> {
let uri = try service[model.serviceKey] ?? ServiceError.noServiceEndpoint(model.serviceKey)
let uri = try service[model.serviceKey].value(or: ServiceError.noServiceEndpoint(model.serviceKey))

let token = try service.tokenPrefix + self.accessToken()

Expand Down
2 changes: 1 addition & 1 deletion Sources/Imperial/Middleware/ImperialMiddleware.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class ImperialMiddleware: Middleware {
do {
_ = try request.accessToken()
return try next.respond(to: request)
} catch let error as Abort {
} catch let error as Abort where error.status == .unauthorized {
guard let redirectPath = redirectPath else {
throw error
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/Imperial/Routing/FederatedServiceRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ public protocol FederatedServiceRouter {
/// - Throws: N/A
func configureRoutes(withAuthURL authURL: String, on router: Router)throws

/// Gets an access token from an OAuth provider.
/// This method is the main body of the `callback` handler.
///
/// - Parameters: request: The request for the route
/// this method is called in.
func fetchToken(from request: Request)throws -> Future<String>

/// The route to call when the user is going to authenticate with the OAuth provider.
/// By default, this route redirects the user to `authURL`.
Expand Down
24 changes: 23 additions & 1 deletion Sources/Imperial/ServiceRegister.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import Vapor

extension Router {

/// Registers an OAuth provider's router with
/// the parent route.
///
/// - Parameters:
/// - provider: The provider who's router will be used.
/// - authUrl: The path to navigate to to authenticate.
/// - callback: The path or URL that the provider with
/// redirect to when authentication completes.
/// - scope: The scopes to get access to on authentication.
/// - completion: A callback with the current request and fetched
/// access token that is called when auth completes.
public func oAuth<OAuthProvider>(
from provider: OAuthProvider.Type,
authenticate authUrl: String,
Expand All @@ -11,14 +23,24 @@ extension Router {
_ = try OAuthProvider(router: self, authenticate: authUrl, callback: callback, scope: scope, completion: completion)
}

/// Registers an OAuth provider's router with
/// the parent route and a redirection callback.
///
/// - Parameters:
/// - provider: The provider who's router will be used.
/// - authUrl: The path to navigate to to authenticate.
/// - callback: The path or URL that the provider with
/// redirect to when authentication completes.
/// - scope: The scopes to get access to on authentication.
/// - redirect: The path/URL to redirect to when auth completes.
public func oAuth<OAuthProvider>(
from provider: OAuthProvider.Type,
authenticate authUrl: String,
callback: String,
scope: [String] = [],
redirect redirectURL: String
)throws where OAuthProvider: FederatedService {
try self.oAuth(from: OAuthProvider.self, authenticate: authUrl, callback: callback, scope: scope) { (request, token) in
try self.oAuth(from: OAuthProvider.self, authenticate: authUrl, callback: callback, scope: scope) { (request, _) in
let redirect: Response = request.redirect(to: redirectURL)
return request.eventLoop.newSucceededFuture(result: redirect)
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/Imperial/Services/GitHub/GitHubAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class GitHubAuth: FederatedServiceTokens {
let idError = ImperialError.missingEnvVar(idEnvKey)
let secretError = ImperialError.missingEnvVar(secretEnvKey)

self.clientID = try Environment.get(idEnvKey) ?? idError
self.clientSecret = try Environment.get(secretEnvKey) ?? secretError
self.clientID = try Environment.get(idEnvKey).value(or: idError)
self.clientSecret = try Environment.get(secretEnvKey).value(or: secretError)
}
}
22 changes: 17 additions & 5 deletions Sources/Imperial/Services/GitHub/GitHubRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class GitHubRouter: FederatedServiceRouter {
self.callbackCompletion = completion
}

public func callback(_ request: Request)throws -> Future<Response> {
public func fetchToken(from request: Request)throws -> Future<String> {
let code: String
if let queryCode: String = try request.query.get(at: "code") {
code = queryCode
Expand All @@ -30,17 +30,29 @@ public class GitHubRouter: FederatedServiceRouter {
}

let body = GitHubCallbackBody(clientId: self.tokens.clientID, clientSecret: self.tokens.clientSecret, code: code)
return try request.make(Client.self).post(accessTokenURL, content: body).flatMap(to: String.self, { (response) in

return try body.encode(using: request).flatMap(to: Response.self) { request in
guard let url = URL(string: self.accessTokenURL) else {
throw Abort(.internalServerError, reason: "Unable to convert String '\(self.accessTokenURL)' to URL")
}
request.http.method = .POST
request.http.url = url
return try request.make(Client.self).send(request)
}.flatMap(to: String.self) { response in
return response.content.get(String.self, at: ["access_token"])
}).flatMap(to: ResponseEncodable.self, { (accessToken) in
}
}

public func callback(_ request: Request)throws -> Future<Response> {
return try self.fetchToken(from: request).flatMap(to: ResponseEncodable.self) { accessToken in
let session = try request.session()

session["access_token"] = accessToken
try session.set("access_token_service", to: OAuthService.github)

return try self.callbackCompletion(request, accessToken)
}).flatMap(to: Response.self, { (response) in
}.flatMap(to: Response.self) { response in
return try response.encode(for: request)
})
}
}
}
4 changes: 2 additions & 2 deletions Sources/Imperial/Services/Google/GoogleAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class GoogleAuth: FederatedServiceTokens {
let idError = ImperialError.missingEnvVar(idEnvKey)
let secretError = ImperialError.missingEnvVar(secretEnvKey)

self.clientID = try Environment.get(idEnvKey) ?? idError
self.clientSecret = try Environment.get(secretEnvKey) ?? secretError
self.clientID = try Environment.get(idEnvKey).value(or: idError)
self.clientSecret = try Environment.get(secretEnvKey).value(or: secretError)
}
}
21 changes: 16 additions & 5 deletions Sources/Imperial/Services/Google/GoogleRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class GoogleRouter: FederatedServiceRouter {
self.callbackCompletion = completion
}

public func callback(_ request: Request)throws -> Future<Response> {
public func fetchToken(from request: Request)throws -> Future<String> {
let code: String
if let queryCode: String = try request.query.get(at: "code") {
code = queryCode
Expand All @@ -32,17 +32,28 @@ public class GoogleRouter: FederatedServiceRouter {
}

let body = GoogleCallbackBody(code: code, clientId: self.tokens.clientID, clientSecret: self.tokens.clientSecret, redirectURI: self.callbackURL)
return try request.make(Client.self).post(accessTokenURL, content: body).flatMap(to: String.self, { (response) in
return try body.encode(using: request).flatMap(to: Response.self) { request in
guard let url = URL(string: self.accessTokenURL) else {
throw Abort(.internalServerError, reason: "Unable to convert String '\(self.accessTokenURL)' to URL")
}
request.http.method = .POST
request.http.url = url
return try request.make(Client.self).send(request)
}.flatMap(to: String.self) { response in
return response.content.get(String.self, at: ["access_token"])
}).flatMap(to: ResponseEncodable.self, { (accessToken) in
}
}

public func callback(_ request: Request)throws -> Future<Response> {
return try self.fetchToken(from: request).flatMap(to: ResponseEncodable.self) { accessToken in
let session = try request.session()

session["access_token"] = accessToken
try session.set("access_token_service", to: OAuthService.google)

return try self.callbackCompletion(request, accessToken)
}).flatMap(to: Response.self, { (response) in
}.flatMap(to: Response.self) { response in
return try response.encode(for: request)
})
}
}
}
Loading

0 comments on commit 9b67da8

Please sign in to comment.