Moya: Refresh Access Token Plugin

Created on 27 Aug 2019  路  6Comments  路  Source: Moya/Moya

hi guys, I would like to ask if what plugin for access token to refresh you are using. Im just new with moya and some of the stuff are using Rx. Do you guys know a much easier way to do it with provider or plugin?

Most helpful comment

so I have this provider extension

import Moya
import Foundation

extension MoyaProvider {

    public convenience init(
        handleRefreshToken: Bool,
        plugins: [PluginType] = [],
        manager: Manager = MoyaProvider<Target>.defaultAlamofireManager() ) {

        if handleRefreshToken {
            self.init(requestClosure: MoyaProvider.endpointResolver(), manager: manager, plugins: plugins)
        } else {
            self.init()
        }
    }

    static func endpointResolver() -> MoyaProvider<Target>.RequestClosure {
        return { (endpoint, closure) in
            //Getting the original request
            let request = try! endpoint.urlRequest()

            //assume you have saved the existing token somewhere
            if (AccessToken.hasValidToked()) {
                // Token is valid, so just resume the original request
                closure(.success(request))
                return
            }

            let authenticationProvider: MoyaProvider<AuthenticationRequest> = MoyaProvider<AuthenticationRequest>()

            guard
                let currentSession = LocalSettings.currentSession
            else {
                return
            }

            //Do a request to refresh the authtoken based on refreshToken
            authenticationProvider.request(.refreshToken(currentSession.refreshToken)) { result in
                switch result {
                case .success(let response):

                    do {

                        let gatewayResponse:  GatewayResponseAPIModel<SignInResponseAPIModel> = try JSONDecoder().decode(GatewayResponseAPIModel<SignInResponseAPIModel>.self, 
                                                                                                                         from: response.data) 

                        guard let body = gatewayResponse.body else {
                            closure(.failure(MoyaError.jsonMapping(response)))
                            return
                        }
                        let userSession = UserSession(accessToken: body.accessToken,
                                                      expiresIn: body.expiresIn,
                                                      refreshToken: body.refreshToken,
                                                      tokenDate: gatewayResponse.date,
                                                      tokenType: body.tokenType,
                                                      userId: body.userId)
                        LocalSettings.currentSession = userSession                        

                        closure(.success(request))
                    } catch let error {
                        closure(.failure(MoyaError.encodableMapping(error)))
                    }

                    // closure(.success(request)) // This line will "resume" the actual request, and then you can use AccessTokenPlugin to set the Authentication header
                case .failure(let error):
                    closure(.failure(error)) //something went terrible wrong! Request will not be performed
                }
            }
        }

    }
}

and a plugin to include my access token in the header

import Foundation
import Moya

public final class AccessTokenPlugin: PluginType {

    // MARK: - Initializer
    public init() { }

    // MARK: - Stored Properties
    private var request: (RequestType, TargetType)?

    // MARK: - Instance Methods
    public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
        var request = request

        guard
            let session = LocalSettings.currentSession
        else { return request }

        request.addValue("Bearer \(session.accessToken)", forHTTPHeaderField: "Authorization")
        return request
    }
}

Usage Will be:

var provider: MoyaProvider<OTPRequest> = MoyaProvider<OTPRequest>(
            handleRefreshToken: true,
            plugins: [
                AccessTokenPlugin()
            ]
        )

Sorry for the slow response

All 6 comments

Hey @vlainvaldez. First of all sorry for the delay in response - things got busy with the newest Moya 14! As for the question, this is a pretty common ask so try to do a search on our issues - I'm sure you will find out a lot of hints!

For instance, this comment lists some issues that might be worth looking into. Hope you find these useful!

Let me know if you had any troubles implementing something like this for your project.

@sunshinejr yes, I solved it by creating my own Plugin by and also having an extension for provider

Awesome. Glad you figured it out @vlainvaldez and thanks for letting me know! 馃帀

Hi @vlainvaldez
I also have this issue,
can you share how you solved it.
thx!!!!

so I have this provider extension

import Moya
import Foundation

extension MoyaProvider {

    public convenience init(
        handleRefreshToken: Bool,
        plugins: [PluginType] = [],
        manager: Manager = MoyaProvider<Target>.defaultAlamofireManager() ) {

        if handleRefreshToken {
            self.init(requestClosure: MoyaProvider.endpointResolver(), manager: manager, plugins: plugins)
        } else {
            self.init()
        }
    }

    static func endpointResolver() -> MoyaProvider<Target>.RequestClosure {
        return { (endpoint, closure) in
            //Getting the original request
            let request = try! endpoint.urlRequest()

            //assume you have saved the existing token somewhere
            if (AccessToken.hasValidToked()) {
                // Token is valid, so just resume the original request
                closure(.success(request))
                return
            }

            let authenticationProvider: MoyaProvider<AuthenticationRequest> = MoyaProvider<AuthenticationRequest>()

            guard
                let currentSession = LocalSettings.currentSession
            else {
                return
            }

            //Do a request to refresh the authtoken based on refreshToken
            authenticationProvider.request(.refreshToken(currentSession.refreshToken)) { result in
                switch result {
                case .success(let response):

                    do {

                        let gatewayResponse:  GatewayResponseAPIModel<SignInResponseAPIModel> = try JSONDecoder().decode(GatewayResponseAPIModel<SignInResponseAPIModel>.self, 
                                                                                                                         from: response.data) 

                        guard let body = gatewayResponse.body else {
                            closure(.failure(MoyaError.jsonMapping(response)))
                            return
                        }
                        let userSession = UserSession(accessToken: body.accessToken,
                                                      expiresIn: body.expiresIn,
                                                      refreshToken: body.refreshToken,
                                                      tokenDate: gatewayResponse.date,
                                                      tokenType: body.tokenType,
                                                      userId: body.userId)
                        LocalSettings.currentSession = userSession                        

                        closure(.success(request))
                    } catch let error {
                        closure(.failure(MoyaError.encodableMapping(error)))
                    }

                    // closure(.success(request)) // This line will "resume" the actual request, and then you can use AccessTokenPlugin to set the Authentication header
                case .failure(let error):
                    closure(.failure(error)) //something went terrible wrong! Request will not be performed
                }
            }
        }

    }
}

and a plugin to include my access token in the header

import Foundation
import Moya

public final class AccessTokenPlugin: PluginType {

    // MARK: - Initializer
    public init() { }

    // MARK: - Stored Properties
    private var request: (RequestType, TargetType)?

    // MARK: - Instance Methods
    public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
        var request = request

        guard
            let session = LocalSettings.currentSession
        else { return request }

        request.addValue("Bearer \(session.accessToken)", forHTTPHeaderField: "Authorization")
        return request
    }
}

Usage Will be:

var provider: MoyaProvider<OTPRequest> = MoyaProvider<OTPRequest>(
            handleRefreshToken: true,
            plugins: [
                AccessTokenPlugin()
            ]
        )

Sorry for the slow response

thanks for your reply!
I will try to follow your suggestion!!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

BasThomas picture BasThomas  路  27Comments

BasThomas picture BasThomas  路  31Comments

Matthijn picture Matthijn  路  27Comments

rlam3 picture rlam3  路  28Comments

swabzz picture swabzz  路  24Comments