์๋ก์ด ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ๊นํ๋ธ ๋ก๊ทธ์ธ์ ํตํด ๋ก๊ทธ์ธํ๊ณ , ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ์ ๋ณด์ ๋ ํฌ์งํ ๋ฆฌ ์ ๋ณด๋ฅผ ๋ฐ์์์ผ ํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ฒ ๋์๋ค.
๋จผ์ , ๊นํ๋ธ์ ๋ด ์ฑ์ ๋ฑ๋กํ์ฌ ClientId์ Client secrets์ ๋ฐ๊ธ ๋ฐ๋๋ค.
์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ด์ค๋ค.
** callback URL์ ๋ด์ฑ์ด๋ฆ://login์ผ๋ก ์ ์ด์ฃผ์ด์ผ ์ ์์ ์ผ๋ก ์๋ํ๋ค.
URL Types์์ URL Schemes์ ๋ด ์ฑ์ด๋ฆ์ ์ ์ด์ค๋ค.
์กฐ๊ธ ๋ค๋ฅธ์ ์ด ์์ง๋ง ์ ์ฒด ๋ก์ง์ด๋ค. ์ข ํน์ดํ ์ ์ Client์์ Provider๋ก code ์์ฒญ, ๋ฐ์ ์ฝ๋๋ก token์ ์์ฒญํด์ 2๋ฒ ์์ฒญํ๋ค. code๋ฅผ ๋ฐ์์ผ๋ก์จ ์ธ๊ฐ๋ ์ฌ์ฉ์์์ ์ธ์ฆ๋ฐ๊ณ , ๊ทธ ๋ค์ ๋ด๊ฐ ์ํ๋ ์ ๋ณด์ธ access token์ ๋ฐ๋๋ค.
๋ก๊ทธ์ธ์ ํ๋๋ฐ ์์ด์ 2๋ฒ์ ์์ฒญ์ด ๋ฒ๊ฑฐ๋ก์ธ ์ ์๋๋ฐ, ์ด๋ implicit grant๋ฅผ ์ฌ์ฉํ๋ฉด code์์ฒญ ์์ด ํ๋ฒ์ ์์ฒญ์ผ๋ก access token์ ๋ฐ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฌ๋ ์ด implicit grant๋ ๋จ์ ์ด ์๋๋ฐ, refresh token์ ์ฌ์ฉํ ์ ์์ด access token์ ๋ฐ๊ธ ์๋ช ์ด ์งง์์ ธ ๋ง๋ฃ๋ ๋ ๋ง๋ค access token์ ์๋ก ์์ฒญํด์ผ ํ๋ค.
์ฑ์์๋ ํค์ฒด์ธ๊ณผ ๊ฐ์ด ํด๋ผ์ด์ธํธ์์ ํ ํฐ ๋ณด๊ด์ ์์ ํ๊ฒ ํ ์ ์์ด implicit grant ๋ฐฉ์์ ์ฌ์ฉํ์ง ์๊ณ , ๋ณดํต ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ฉํ๋ค.
๋ธ๋ผ์ฐ์ ๋ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ๊ด๋ฆฌํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์ ์ด ์ ๋ณด๋ฅผ ์ต์ํ์ผ๋ก ๋ ธ์ถ์ํค๊ธฐ ์ํด codeํธ์ถ์ ์๋ตํ๊ณ ๋ฐ๋ก access token์ ๋ฐ์ ์ ๋ณด ๋ ธ์ถ์ ๊ฐ์ํํ๋ค.
๋์ access token์ ์๋ช ์ ์งง๊ฒํด ๊ทธ๋๋ง ์์ ํ๊ฒ ์ ์งํ๋๋ก ํ๋ค.
์ ์ฒด์ฝ๋
LoginManager.swift
import Foundation
import KeychainSwift
import Alamofire
import UIKit
class LoginManager {
static let shared = LoginManager()
private let client_id: String = ""
private let client_secret: String = ""
private let scope: String = "repo gist user"
private let githubURL: String = "https://github.com"
private let githubApiURL: String = "https://api.github.com"
func requestCode() {
var components = URLComponents(string: githubURL+ApiPath.LOGIN.rawValue)!
components.queryItems = [
URLQueryItem(name: "client_id", value: self.client_id),
URLQueryItem(name: "scope", value: self.scope),
]
let urlString = components.url?.absoluteString
if let url = URL(string: urlString!), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
func requestAccessToken(with code: String) {
let parameters = ["client_id": client_id,
"client_secret": client_secret,
"code": code]
let headers: HTTPHeaders = ["Accept": "application/json"]
AF.request(githubURL+ApiPath.ACCESS_TOKEN.rawValue,
method: .post, parameters: parameters,
headers: headers).responseJSON { (response) in
switch response.result {
case let .success(json):
if let dic = json as? [String: String] {
let accessToken = dic["access_token"] ?? ""
KeychainSwift().set(accessToken, forKey: "accessToken")
}
case let .failure(error):
print(error)
}
}
}
func getUser() {
let accessToken = KeychainSwift().get("accessToken") ?? ""
let headers: HTTPHeaders = ["Accept": "application/vnd.github.v3+json",
"Authorization": "token \(accessToken)"]
AF.request(githubApiURL+ApiPath.USER.rawValue,
method: .get,
parameters: [:],
headers: headers).responseJSON(completionHandler: { (response) in
switch response.result {
case .success(let json):
print(json as! [String: Any])
case .failure:
print("")
}
})
}
func getRepos() {
let accessToken = KeychainSwift().get("accessToken") ?? ""
let headers: HTTPHeaders = ["Accept": "application/vnd.github.v3+json",
"Authorization": "token \(accessToken)"]
AF.request(githubApiURL+ApiPath.REPOS.rawValue,
method: .get, parameters: [:],
headers: headers).responseJSON(completionHandler: { (response) in
switch response.result {
case .success(let json):
print(json)
case .failure:
print("")
}
})
}
func logout() {
KeychainSwift().clear()
}
}
ApiPath.swift
import Foundation
enum ApiPath: String {
case LOGIN = "/login/oauth/authorize" // ์ฌ์ฉ์์ ๊นํ๋ธ ์์ด๋ (์ฌํ๋ฆฌ ํ์ด์ง ์ด๋)
case ACCESS_TOKEN = "/login/oauth/access_token" // access token ์์ฒญ
case USER = "/user" // ์ ์ ์ ๋ณด
case REPOS = "/user/repos" // ์ ์ ๋ ํฌ์งํ ๋ฆฌ ์ ๋ณด
}
SceneDelegate.swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
let code = url.absoluteString.components(separatedBy: "code=").last ?? ""
LoginManager.shared.requestAccessToken(with: code)
}
}
(github documentation ์ฐธ๊ณ )
1) ๋ก๊ทธ์ธ ์์ฒญ
์ฌ์ฉ์๊ฐ Github Login ๋ฒํผ์ ํด๋ฆญํ์๋
@IBAction func login(_ sender: Any) {
LoginManager.shared.requestCode()
}
func requestCode() {
var components = URLComponents(string: githubURL+ApiPath.LOGIN.rawValue)!
components.queryItems = [
URLQueryItem(name: "client_id", value: self.client_id),
URLQueryItem(name: "scope", value: self.scope),
]
let urlString = components.url?.absoluteString
if let url = URL(string: urlString!), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
2) code ์์ฒญ
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
let code = url.absoluteString.components(separatedBy: "code=").last ?? ""
LoginManager.shared.requestAccessToken(with: code)
}
}
3) ๋ฐ์ ์ฝ๋๋ก access token ์์ฒญ
func requestAccessToken(with code: String) {
let parameters = ["client_id": client_id,
"client_secret": client_secret,
"code": code]
let headers: HTTPHeaders = ["Accept": "application/json"]
AF.request(githubURL+ApiPath.ACCESS_TOKEN.rawValue,
method: .post, parameters: parameters,
headers: headers).responseJSON { (response) in
switch response.result {
case let .success(json):
if let dic = json as? [String: String] {
let accessToken = dic["access_token"] ?? ""
KeychainSwift().set(accessToken, forKey: "accessToken")
}
case let .failure(error):
print(error)
}
}
}
+์ถ๊ฐ) ๋ฐ์ access token์ผ๋ก user์ repo์ ๋ณด ์์ฒญ
func getUser() {
let accessToken = KeychainSwift().get("accessToken") ?? ""
let headers: HTTPHeaders = ["Accept": "application/vnd.github.v3+json",
"Authorization": "token \(accessToken)"]
AF.request(githubApiURL+ApiPath.USER.rawValue,
method: .get,
parameters: [:],
headers: headers).responseJSON(completionHandler: { (response) in
switch response.result {
case .success(let json):
print(json as! [String: Any])
case .failure:
print("")
}
})
}
func getRepos() {
let accessToken = KeychainSwift().get("accessToken") ?? ""
let headers: HTTPHeaders = ["Accept": "application/vnd.github.v3+json",
"Authorization": "token \(accessToken)"]
AF.request(githubApiURL+ApiPath.REPOS.rawValue,
method: .get, parameters: [:],
headers: headers).responseJSON(completionHandler: { (response) in
switch response.result {
case .success(let json):
print(json)
case .failure:
print("")
}
})
}
์ฐธ๊ณ
https://eunjin3786.tistory.com/211
https://zeddios.tistory.com/1102