diff --git a/Package.swift b/Package.swift index 142ac2e..5cf83ae 100644 --- a/Package.swift +++ b/Package.swift @@ -7,11 +7,11 @@ let package = Package( name: "OAuth2"), ], dependencies: [ - .Package(url: "https://github.com/PerfectlySoft/PerfectLib.git", majorVersion: 2), - .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTP.git", majorVersion: 2), - .Package(url: "https://github.com/PerfectlySoft/Perfect-Logger.git", majorVersion: 1), - .Package(url: "https://github.com/iamjono/SwiftString.git", majorVersion: 1), - .Package(url: "https://github.com/PerfectlySoft/Perfect-Session.git", majorVersion: 1), + .Package(url: "https://github.com/PerfectlySoft/PerfectLib.git", majorVersion: 3), + .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTP.git", majorVersion: 3), + .Package(url: "https://github.com/PerfectlySoft/Perfect-Logger.git", majorVersion: 3), + .Package(url: "https://github.com/iamjono/SwiftString.git", majorVersion: 2), + .Package(url: "https://github.com/PerfectlySoft/Perfect-Session.git", majorVersion: 3), ] ) diff --git a/Sources/OAuth2/OAuth2.swift b/Sources/OAuth2/OAuth2.swift index 350cf35..ab04229 100644 --- a/Sources/OAuth2/OAuth2.swift +++ b/Sources/OAuth2/OAuth2.swift @@ -92,7 +92,47 @@ open class OAuth2 { return try exchange(authorizationCode: AuthorizationCode(code: code, redirectURL: redirectURL)) } - // TODO: add refresh token support + + /// Renew Access token by using the previously fetched refresh token + /// - Parameters: + /// - refreshToken: the refresh token used to renew the access token + /// - needSecret: Some providers do not need a client secret (i.e. Microsoft) + /// - scopes: the scopes used with the original access token (optional) + /// - Throws: + /// - InvalidAPIResponse(): if the server does not respond in a way we expect + /// - OAuth2Error() if the oauth server calls back with an error + + func refresh(refreshToken: String, needSecret: Bool = false, scopes: [String] = []) throws -> OAuth2Token { + + var postBody = ["grant_type": "refresh_token", + "client_id": clientID, + "refresh_token": refreshToken] + + if !scopes.isEmpty { + postBody["scope"] = scopes.joined(separator: " ") + } + + if needSecret { + postBody["client_secret"] = clientSecret + } + + var httpBody = URLComponents() + httpBody.queryItems = postBody.map { key, value in + return URLQueryItem(name: key, value: value) + } + + let data = makeRequest(.post, tokenURL, body: httpBody.percentEncodedQuery!, encoding: "form") + guard let token = OAuth2Token(json: data) else { + if let error = OAuth2Error(json: data) { + throw error + } else { + throw InvalidAPIResponse() + } + } + return token + + } + } extension URLComponents { diff --git a/Sources/OAuth2/OAuth2Error.swift b/Sources/OAuth2/OAuth2Error.swift index fa3fde1..555471a 100644 --- a/Sources/OAuth2/OAuth2Error.swift +++ b/Sources/OAuth2/OAuth2Error.swift @@ -24,7 +24,7 @@ public struct OAuth2Error: Error { } /// Convenience initializer from JSON - init?(json: [String: Any]) { + public init?(json: [String: Any]) { guard let errorCode = json["error"] as? String, let code = OAuth2ErrorCode(rawValue: errorCode) else { return nil diff --git a/Sources/OAuth2/makeRequest.swift b/Sources/OAuth2/makeRequest.swift index 9648058..7190900 100644 --- a/Sources/OAuth2/makeRequest.swift +++ b/Sources/OAuth2/makeRequest.swift @@ -23,7 +23,7 @@ extension OAuth2 { /// - body: The JSON formatted sring to sent to the server /// Response: /// (HTTPResponseStatus, "data" - [String:Any], "raw response" - [String:Any], HTTPHeaderParser) - func makeRequest( + open func makeRequest( _ method: HTTPMethod, _ url: String, body: String = "", diff --git a/Tests/AuthTests/AuthProvidersTests.swift b/Tests/AuthTests/AuthProvidersTests.swift index feb5bbf..e98c7af 100644 --- a/Tests/AuthTests/AuthProvidersTests.swift +++ b/Tests/AuthTests/AuthProvidersTests.swift @@ -1,5 +1,5 @@ import XCTest -@testable import AuthProviders +@testable import OAuth2 class AuthProvidersTests: XCTestCase {