Skip to content

Instantly share code, notes, and snippets.

@ylem
Last active April 10, 2023 20:43
Show Gist options
  • Save ylem/765c4822f20b16fe882b03db0adb994f to your computer and use it in GitHub Desktop.
Save ylem/765c4822f20b16fe882b03db0adb994f to your computer and use it in GitHub Desktop.
OAuth login
import Foundation
import Combine
class OAuthLogin {
static let shared = OAuthLogin()
let baseURL = "https://your-auth-server.com"
let clientID = "your-client-id"
let clientSecret = "your-client-secret"
let scope = "your-scope"
var accessToken: String?
var refreshToken: String?
private var cancellable: AnyCancellable?
private init() {}
func login(username: String, password: String) -> AnyPublisher<Void, Error> {
let parameters = [
"grant_type": "password",
"username": username,
"password": password,
"scope": scope
]
let headers = ["Authorization": "Basic \(base64EncodedCredentials())"]
let url = URL(string: "\(baseURL)/oauth/token")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = parameters.percentEncoded()
return URLSession.shared.dataTaskPublisher(for: request)
.map(\.data)
.decode(type: TokenResponse.self, decoder: JSONDecoder())
.handleEvents(receiveOutput: { [weak self] tokenResponse in
self?.accessToken = tokenResponse.accessToken
self?.refreshToken = tokenResponse.refreshToken
})
.map { _ in }
.catch { error -> Empty<Void, Error> in
return Empty(completeImmediately: true)
}
.eraseToAnyPublisher()
}
private func base64EncodedCredentials() -> String {
let credentialsData = "\(clientID):\(clientSecret)".data(using: .utf8)!
let base64Credentials = credentialsData.base64EncodedString()
return base64Credentials
}
}
struct TokenResponse: Codable {
let accessToken: String
let refreshToken: String
let expiresIn: Int
private enum CodingKeys: String, CodingKey {
case accessToken = "access_token"
case refreshToken = "refresh_token"
case expiresIn = "expires_in"
}
}
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "&=")
return allowed
}()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment