Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revoke refresh token instead on log out #45

Merged
merged 1 commit into from
Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Revoke refresh token instead on log out
  • Loading branch information
onevcat committed Apr 15, 2019
commit f578692153bcae96d1441b4be0897d7c66636081
4 changes: 2 additions & 2 deletions LineSDK/LineSDK/Login/LoginManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@ public class LoginManager {
}
}

/// Logs out the current user by revoking the access token.
/// Logs out the current user by revoking the refresh token and all its corresponding access tokens.
///
/// - Parameter completion: The completion closure to be invoked when the logout action is finished.
public func logout(completionHandler completion: @escaping (Result<(), LineSDKError>) -> Void) {
API.revokeAccessToken(completionHandler: completion)
API.revokeRefreshToken(completionHandler: completion)
}

/// Asks this `LoginManager` object to handle a URL callback from either the LINE app or the web login flow.
Expand Down
49 changes: 34 additions & 15 deletions LineSDK/LineSDK/Login/Request/PostRevokeTokenRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,32 @@

import Foundation

struct PostRevokeTokenRequest: Request {
protocol RevokeTokenRequest: Request {
var channelID: String { get }
}

extension RevokeTokenRequest {
var method: HTTPMethod { return .post }
var path: String { return "/oauth2/v2.1/revoke" }
var contentType: ContentType { return .formUrlEncoded }
var authentication: AuthenticateMethod { return .none }
var prefixPipelines: [ResponsePipeline]? {

// Convert empty data to an empty JSON `{}`
let isDataEmpty: ((Data) -> Bool) = { $0.isEmpty }
let dataTransformer = DataTransformRedirector(condition: isDataEmpty) { _ in
return "{}".data(using: .utf8)!
}
return [
.redirector(dataTransformer)
]
}
}

struct PostRevokeTokenRequest: RevokeTokenRequest {
let channelID: String
let accessToken: String

let method: HTTPMethod = .post
let path = "/oauth2/v2.1/revoke"
let contentType: ContentType = .formUrlEncoded
let authentication: AuthenticateMethod = .none

var parameters: [String : Any]? {
return [
"client_id": channelID,
Expand All @@ -38,16 +55,18 @@ struct PostRevokeTokenRequest: Request {
}

typealias Response = Unit

var prefixPipelines: [ResponsePipeline]? {

// Convert empty data to an empty JSON `{}`
let isDataEmpty: ((Data) -> Bool) = { $0.isEmpty }
let dataTransformer = DataTransformRedirector(condition: isDataEmpty) { _ in
return "{}".data(using: .utf8)!
}
}

struct PostRevokeRefreshTokenRequest: RevokeTokenRequest {
let channelID: String
let refreshToken: String

var parameters: [String : Any]? {
return [
.redirector(dataTransformer)
"client_id": channelID,
"refresh_token": refreshToken
]
}

typealias Response = Unit
}
58 changes: 58 additions & 0 deletions LineSDK/LineSDK/Networking/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,64 @@ public struct API {
}
}
}

/// Revokes the refresh token and all its corresponding access tokens.
///
/// - Parameters:
/// - refreshToken: The refresh token to be revoked. Optional. If not specified, the current refresh token will
/// be revoked.
/// - queue: The callback queue that is used for `completion`. The default value is
/// `.currentMainOrAsync`. For more information, see `CallbackQueue`.
/// - completion: The completion closure to be invoked when the access token is revoked.
/// - Note:
///
/// Do not pass an access token to the `refreshToken` parameter. To revoke an access token, use
/// `revokeAccessToken(_:callbackQueue:completionHandler:)` instead.
///
/// The revoked token will be automatically removed from the keychain. If `refreshToken` has a `nil` value
/// and the current refresh token does not exist, `completion` will be called with `.success`. The
/// same applies when `refreshToken` has an invalid refresh token.
///
/// This API will revoke the given refresh token and all its corresponding access token. Once these tokens are
/// revoked, you can neither call an API protected by an access token or refresh the access token with the refresh
/// token. To access the resource owner's content, you need to ask your users to authorize you app again.
///
/// The `LineSDKAccessTokenDidRemove` notification will be sent when the access token removed from the device.
public static func revokeRefreshToken(
_ refreshToken: String? = nil,
callbackQueue queue: CallbackQueue = .currentMainOrAsync,
completionHandler completion: @escaping (Result<(), LineSDKError>) -> Void)
{
func handleSuccessResult() {
let result = Result { try AccessTokenStore.shared.removeCurrentAccessToken() }
completion(result)
}

guard let refreshToken = refreshToken ?? AccessTokenStore.shared.current?.refreshToken else {
// No token input or found in store, just recognize it as success.
queue.execute { completion(.success(())) }
return
}
let request = PostRevokeRefreshTokenRequest(
channelID: LoginConfiguration.shared.channelID,
refreshToken: refreshToken)
Session.shared.send(request, callbackQueue: queue) { result in
switch result {
case .success(_):
handleSuccessResult()
case .failure(let error):
guard case .responseFailed(reason: .invalidHTTPStatusAPIError(let detail)) = error else {
completion(.failure(error))
return
}
// We recognize response 400 as a success for revoking (since the token itself is invalid).
if detail.code == 400 {
Log.print(error.localizedDescription)
handleSuccessResult()
}
}
}
}

/// Verifies the access token.
///
Expand Down