Commit ba64436d authored by Mauro E. Bender's avatar Mauro E. Bender

WIP #10 - Add credentials

parent 4222277c
//
// AccessToken.swift
// Pods
//
// Created by Mauro Bender on 19/10/16.
//
//
import Foundation
public protocol AccessToken {
init (dictionary: [String: AnyObject]) throws
var headerString: String { get }
var accessToken: String { get set }
var refreshToken: String? { get set }
var type: String { get set }
var expiresIn: Int { get set }
var createdAt: Date { get set }
}
extension AccessToken {
var isExpired: Bool { get { return createdAt.addingTimeInterval(TimeInterval(expiresIn)) > Date() } }
var refreshCredentials: Credentials? {
get {
guard let refreshToken = refreshToken else { return nil }
return Credentials (refreshToken: refreshToken)
}
}
}
struct GenricAccessToken: AccessToken {
public var type: String
public var accessToken: String
public var refreshToken: String?
public var expiresIn: Int
public var createdAt: Date
public var headerString: String { return "Bearer \(accessToken)" }
public init(dictionary: [String : AnyObject]) throws {
accessToken = dictionary ["access_token"] as! String
expiresIn = dictionary ["expires_in"] as! Int
type = dictionary ["token_type"] as! String
if let refToken = dictionary ["refresh_token"] as? String {
refreshToken = refToken
}
createdAt = Date()
}
}
......@@ -15,10 +15,12 @@ open class Api {
let apiPath: String
var apiURL: URL { return baseURL.appendingPathComponent( apiPath ) }
var defaultHeaders: [String: String]
public init( baseURL: URL, apiPath: String ) {
self.baseURL = baseURL
self.apiPath = apiPath
self.defaultHeaders = [String: String] ()
}
// RESOURCE
......@@ -29,49 +31,62 @@ open class Api {
// REQUESTS
open func request( _ method: ApiMethod, path: String,
parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = URLEncoding.default,
headers: [String: String]? = nil, completion: @escaping (DataResponse<Any>) -> Void )
headers: [String: String]? = nil, appendPath: Bool = true, completion: @escaping (DataResponse<Any>) -> Void )
{
Alamofire.request( urlForPath( path ).absoluteString, method: method, parameters: parameters,
encoding: encoding, headers: headers ).responseJSON( completionHandler: completion )
let requestHeaders = prepareRequestHeaders(headers: headers)
Alamofire.request( urlForPath( path, appendPath: appendPath ).absoluteString, method: method, parameters: parameters,
encoding: encoding, headers: requestHeaders ).responseJSON( completionHandler: completion )
}
open func get( _ path: String, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = URLEncoding.default,
headers: [String: String]? = nil, completion: @escaping (DataResponse<Any>) -> Void )
headers: [String: String]? = nil, appendPath: Bool = true, completion: @escaping (DataResponse<Any>) -> Void )
{
return request( .get, path: path, parameters: parameters, encoding: encoding,
headers: headers, completion: completion)
headers: headers, appendPath: appendPath, completion: completion)
}
open func post( _ path: String, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = URLEncoding.default,
headers: [String: String]? = nil, completion: @escaping (DataResponse<Any>) -> Void )
headers: [String: String]? = nil, appendPath: Bool = true, completion: @escaping (DataResponse<Any>) -> Void )
{
return request( .post, path: path, parameters: parameters, encoding: encoding,
headers: headers, completion: completion)
headers: headers, appendPath: appendPath, completion: completion)
}
open func put( _ path: String, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = URLEncoding.default,
headers: [String: String]? = nil, completion: @escaping (DataResponse<Any>) -> Void )
headers: [String: String]? = nil, appendPath: Bool = true, completion: @escaping (DataResponse<Any>) -> Void )
{
return request( .put, path: path, parameters: parameters, encoding: encoding,
headers: headers, completion: completion)
headers: headers, appendPath: appendPath, completion: completion)
}
open func patch( _ path: String, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = URLEncoding.default,
headers: [String: String]? = nil, completion: @escaping (DataResponse<Any>) -> Void )
headers: [String: String]? = nil, appendPath: Bool = true, completion: @escaping (DataResponse<Any>) -> Void )
{
return request( .patch, path: path, parameters: parameters, encoding: encoding,
headers: headers, completion: completion)
headers: headers, appendPath: appendPath, completion: completion)
}
open func delete( _ path: String, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = URLEncoding.default,
headers: [String: String]? = nil, completion: @escaping (DataResponse<Any>) -> Void )
headers: [String: String]? = nil, appendPath: Bool = true, completion: @escaping (DataResponse<Any>) -> Void )
{
return request( .delete, path: path, parameters: parameters, encoding: encoding,
headers: headers, completion: completion)
headers: headers, appendPath: appendPath, completion: completion)
}
func urlForPath( _ path: String ) -> URL {
return apiURL.appendingPathComponent( path )
func prepareRequestHeaders(headers: [String: String]?) -> [String: String] {
guard let headers = headers else { return defaultHeaders }
var requestHeaders = [String: String]()
requestHeaders += defaultHeaders
requestHeaders += headers
return requestHeaders
}
func urlForPath( _ path: String, appendPath: Bool = true ) -> URL {
let baseURL = appendPath ? self.apiURL : self.baseURL;
return baseURL.appendingPathComponent( path )
}
}
......
//
// Authenticator.swift
// Pods
//
// Created by Mauro Bender on 19/10/16.
//
//
import Foundation
public enum AuthError: Error {
case apiError (apiError: ApiError)
case invalidRefreshToken ()
case invalidToken ()
}
public enum AuthResult<T: AccessToken> {
case success(accessToken: T)
case failure(error: AuthError)
}
class Authenticator {
var api: Api
var authPath: String
init (api: Api, authPath: String) {
self.api = api
self.authPath = authPath
}
open func authenticate<T: AccessToken> (credentials: Credentials, callback: @escaping ((_ result: AuthResult<T>) -> Void)) {
let credentialsData = credentials.serialize() as [String: AnyObject]
api.post (authPath, parameters: credentialsData, appendPath: false) { response in
switch response.result {
case .success(let data):
if let data = data as? [String: AnyObject], let accessToken = try? T(dictionary: data) {
self.api.defaultHeaders ["Authorization"] = accessToken.headerString
callback(.success(accessToken: accessToken))
} else {
callback(.failure(error: .invalidToken()))
}
break;
case .failure(_):
callback(.failure(error: .apiError(apiError: ApiError(response: response))))
break;
}
}
}
open func refresh<T: AccessToken> (accessToken: T, callback: @escaping ((_ result: AuthResult<T>) -> Void)) {
guard let refreshCredentials = accessToken.refreshCredentials else {
callback(.failure(error: .invalidRefreshToken()))
return
}
authenticate(credentials: refreshCredentials, callback: callback)
}
}
//
// Credentials.swift
// Pods
//
// Created by Mauro Bender on 25/8/16.
//
//
struct Credentials {
let grantType: String
let data: [ String: String ]
func serialize() -> [ String: String ] {
var data = self.data
data += [ "grant_type": grantType ]
return data
}
}
extension Credentials {
init( username: String, password: String, extra: [ String: String ]? = nil ) {
self.grantType = "password"
var data = [ "username": username, "password": password ]
if extra != nil { data += extra }
self.data = data
}
init( assertion: String, extra: [ String: String ]? = nil ) {
self.grantType = "assertion"
var data = [ "assertion": assertion ]
if extra != nil { data += extra }
self.data = data
}
init( refreshToken: String, extra: [ String: String ]? = nil ) {
self.grantType = "refresh_token"
var data = [ "refresh_token": refreshToken ]
if extra != nil { data += extra }
self.data = data
}
public static func password( username: String, password: String, extra: [ String: String ]? = nil ) -> Credentials {
return Credentials( username: username, password: password, extra: extra )
}
public static func facebook( token: String, extra: [ String: String ]? = nil ) -> Credentials {
return Credentials( assertion: token, extra: extra )
}
public static func refreshToken( refreshToken: String, extra: [ String: String ]? = nil ) -> Credentials {
return Credentials( refreshToken: refreshToken, extra: extra )
}
}
......@@ -8,7 +8,9 @@
import Foundation
func += <K, V> (left: inout [K:V], right: [K:V]) {
func += <K, V> (left: inout [K:V], right: [K:V]?) {
guard let right = right else { return }
for (k, v) in right {
left.updateValue(v, forKey: k)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment