Skip to content

Commit

Permalink
Merge pull request #46 from dannys42/Auth0
Browse files Browse the repository at this point in the history
Auth0
  • Loading branch information
0xTim authored Jan 28, 2020
2 parents 9cbd4a1 + 29b1d5b commit d71416e
Show file tree
Hide file tree
Showing 11 changed files with 402 additions and 0 deletions.
24 changes: 24 additions & 0 deletions Sources/Imperial/Services/Auth0/Auth0.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Vapor

public class Auth0: FederatedService {
public var tokens: FederatedServiceTokens
public var router: FederatedServiceRouter

@discardableResult
public required init(
router: Router,
authenticate: String,
authenticateCallback: ((Request)throws -> (Future<Void>))?,
callback: String,
scope: [String] = [],
completion: @escaping (Request, String)throws -> (Future<ResponseEncodable>)
)throws {
self.router = try Auth0Router(callback: callback, completion: completion)
self.tokens = self.router.tokens

self.router.scope = scope
try self.router.configureRoutes(withAuthURL: authenticate, authenticateCallback: authenticateCallback, on: router)

OAuthService.register(.auth0)
}
}
20 changes: 20 additions & 0 deletions Sources/Imperial/Services/Auth0/Auth0Auth.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Vapor

public class Auth0Auth: FederatedServiceTokens {
public static var domain: String = "AUTH0_DOMAIN"
public static var idEnvKey: String = "AUTH0_CLIENT_ID"
public static var secretEnvKey: String = "AUTH0_CLIENT_SECRET"
public var domain: String
public var clientID: String
public var clientSecret: String

public required init() throws {
let domainError = ImperialError.missingEnvVar(Auth0Auth.domain)
let idError = ImperialError.missingEnvVar(Auth0Auth.idEnvKey)
let secretError = ImperialError.missingEnvVar(Auth0Auth.secretEnvKey)

self.domain = try Environment.get(Auth0Auth.domain).value(or: domainError)
self.clientID = try Environment.get(Auth0Auth.idEnvKey).value(or: idError)
self.clientSecret = try Environment.get(Auth0Auth.secretEnvKey).value(or: secretError)
}
}
19 changes: 19 additions & 0 deletions Sources/Imperial/Services/Auth0/Auth0CallbackBody.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Vapor

struct Auth0CallbackBody: Content {
let clientId: String
let clientSecret: String
let code: String
let redirectURI: String
let grantType: String = "authorization_code"

static var defaultContentType: MediaType = .urlEncodedForm

enum CodingKeys: String, CodingKey {
case clientId = "client_id"
case clientSecret = "client_secret"
case code = "code"
case redirectURI = "redirect_uri"
case grantType = "grant_type"
}
}
86 changes: 86 additions & 0 deletions Sources/Imperial/Services/Auth0/Auth0Router.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import Vapor
import Foundation

public class Auth0Router: FederatedServiceRouter {
public let baseURL: String
public let tokens: FederatedServiceTokens
public let callbackCompletion: (Request, String)throws -> (Future<ResponseEncodable>)
public var scope: [String] = [ ]
public var requiredScopes = [ "openid" ]
public let callbackURL: String
public let accessTokenURL: String

private func providerUrl(path: String) -> String {
return self.baseURL.finished(with: "/") + path
}

public required init(callback: String, completion: @escaping (Request, String)throws -> (Future<ResponseEncodable>)) throws {
let auth = try Auth0Auth()
self.tokens = auth
self.baseURL = "https://\(auth.domain)"
self.accessTokenURL = baseURL.finished(with: "/") + "oauth/token"
self.callbackURL = callback
self.callbackCompletion = completion
}

public func authURL(_ request: Request) throws -> String {
let path="authorize"

var params=[
"response_type=code",
"client_id=\(self.tokens.clientID)",
"redirect_uri=\(self.callbackURL)",
]

let allScopes = self.scope + self.requiredScopes
let scopeString = allScopes.joined(separator: " ").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
if let scopes = scopeString {
params += [ "scope=\(scopes)" ]
}

let rtn = self.providerUrl(path: path + "?" + params.joined(separator: "&"))
return rtn
}

public func fetchToken(from request: Request)throws -> Future<String> {
let code: String
if let queryCode: String = try request.query.get(at: "code") {
code = queryCode
} else if let error: String = try request.query.get(at: "error") {
throw Abort(.badRequest, reason: error)
} else {
throw Abort(.badRequest, reason: "Missing 'code' key in URL query")
}

let body = Auth0CallbackBody(clientId: self.tokens.clientID,
clientSecret: self.tokens.clientSecret,
code: code,
redirectURI: self.callbackURL)

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
request.http.contentType = .urlEncodedForm

return try request.make(Client.self).send(request)
}.flatMap(to: String.self) { response in
return response.content.get(String.self, at: ["access_token"])
}
}

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.setAccessToken(accessToken)
try session.set("access_token_service", to: OAuthService.auth0)

return try self.callbackCompletion(request, accessToken)
}.flatMap(to: Response.self) { response in
return try response.encode(for: request)
}
}
}
6 changes: 6 additions & 0 deletions Sources/Imperial/Services/Auth0/Service+Auth0.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extension OAuthService {
public static let auth0 = OAuthService.init(
name: "auth0",
endpoints: [:]
)
}
Binary file added docs/Auth0/Applications-Marked.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit d71416e

Please sign in to comment.