Skip to content

iOS Moya Implementing OAuth 2.0 Requests

Published: at 05:17 AM

With the release of Swift 5 and Alamofire 5, the method of implementing Moya + Oauth 2.0 has also changed slightly. The overall framework still follows the content of the previous article, only updating some content that needs to be handled.

1. Alamofire part

Alamofire has deprecated SessionManager, favoring Session, so the related code needs to be modified:

func getManager() -> SessionManager {
        let oauth2 = OAuth2CodeGrant(settings: [
            "client_id": "my_swift_app",
            "client_secret": "C7447242",
            "authorize_uri": "https://github.com/login/oauth/authorize",
            "token_uri": "https://github.com/login/oauth/access_token",   // code grant only
            "redirect_uris": ["myapp://oauth/callback"],   // register your own "myapp" scheme in Info.plist
            "scope": "user repo:status",
            "secret_in_body": true,    // Github needs this
            "keychain": false,         // if you DON'T want keychain integration
            ] as OAuth2JSON)

        let oauthHandler = OAuth2Handler(oauth2: oauth2)
        let interceptor = Interceptor(adapter: oauthHandler, retrier: oauthHandler)
        let session = Alamofire.Session(interceptor: interceptor)
        return session
}

2. Oauth part

Modify OAuth2Handler code, since the protocal singature of RequestRetrier and RequestAdapter had been changed, so we need to implementing them both:

extension OAuth2Handler: RequestRetrier {
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {

        if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401, let req = request.request {
            var dataRequest = OAuth2DataRequest(request: req, callback: { _ in })
            dataRequest.context = completion
            loader.enqueue(request: dataRequest)
            loader.attemptToAuthorize { authParams, _ in
                self.loader.dequeueAndApply { req in
                    if let comp = req.context as? ((RetryResult) -> Void) {
                        let result = (authParams != nil) ? RetryResult.retry : RetryResult.doNotRetry
                        comp(result)
                    }
                }
            }
        } else {
            completion(.doNotRetry)
        }
    }
}
extension OAuth2Handler: RequestAdapter {

    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) {

        guard loader.oauth2.accessToken != nil else {
            completion(.failure(NSError(domain:"", code:0, userInfo:nil)))
            return
        }

        if let signedRequest = try? urlRequest.signed(with: loader.oauth2) {
            completion(.success(signedRequest))
        } else {
            completion(.failure(NSError(domain:"", code:0, userInfo:nil)))
        }
    }
}

3. Catalyst part

The existing solution can be directly applied to the Catalyst project, but in my actual test, I found that iOS projects (iPhone/iPad) can make normal requests, while macOS can authenticate successfully but cannot make normal requests after authentication. After troubleshooting, I found that it was because the Oauth token was saved using Keychain: “keychain”: true, if you want to use it in macOS, you need to turn on “Keychain Sharing”.

1hhYBs