Harry Blue
Harry Blue

Reputation: 4522

iOS Swift 4 OAuth

I writing an app in Swift 4 that uses the Discogs API. As such, I require a user to have access to personal data on their Discogs account, so I am authenticating against their API using OAuthSwift. Currently, I am able to kick off the auth flow, sign in and return the an oauthToken and the oauthTokenSecret

Making a subsequent request to their https://api.discogs.com/oauth/identity I am returned a user object, so I am happy at this point I can sign in and make authenticated requests.

However, I do not understand how I can check if a user is authenticated when the app first starts up. Currently, I am not storing the response, instead I am making a call to the identity endpoint in nested callback

import UIKit
import OAuthSwift

class ViewController: UIViewController {

    let oauthSwift = OAuth1Swift(
        consumerKey: "foo",
        consumerSecret: "bar",
        requestTokenUrl: "https://api.discogs.com/oauth/request_token",
        authorizeUrl: "https://www.discogs.com/oauth/authorize",
        accessTokenUrl: "https://api.discogs.com/oauth/access_token"
    )

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        view.backgroundColor = .white

        kickOffAuthFlow()

    }

    fileprivate func kickOffAuthFlow() {

        oauthSwift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthSwift)

        guard let callbackURL = URL(string: "foo.bar.boobaz:/oauth_callback") else { return }

        oauthSwift.authorize(withCallbackURL: callbackURL, success: { (credential, response, parameters) in
            _ = self.oauthSwift.client.get("https://api.discogs.com/oauth/identity", success: { (response) in
                guard let dataString = response.string else { return }
                print(dataString)
            }, failure: { (error) in
                print("error")
            })
        }) { (error) in
            print(error.localizedDescription)
        }
    }
}

What is best practice in this case? How should I store these tokens and how should I ensure once the user is logged in, they aren't forced to log in the next time the app is opened (providing the token hasn't expired, however that is a separate issue I am prepared to handle at a later point)

Coming from a web development background, I was able to just store a token in session storage, on load I would then check the exp on the token and request a new one or take some other action.

I have not quite grasped how this works in iOS development yet.

Upvotes: 1

Views: 3971

Answers (1)

Hitesh Agarwal
Hitesh Agarwal

Reputation: 2015

You have two options to store access token in local.

  1. UserDefault
  2. Keychain

1. UserDefault

Use UserDefault to store token in memory. When the app gets launch, check if the token is stored in userdafault. UserDefault is used as short memory storage where you can store small data. It remains in memory if you kill the app.

    let tokenIdentifier = "TokenIdentifier"
    func storeAccessToken(token: String) {
        UserDefaults.standard.set(token, forKey: tokenIdentifier)
    }

    func checkUserLogin() {
        if UserDefaults.standard.value(forKey: tokenIdentifier) != nil {
            print("User is Login")
        }
        else {
            print("User need to login")
        }
    }

check this for learn more about userdefault

https://swift3tutorials.com/swift-3-user-defaults/

https://www.hackingwithswift.com/example-code/system/how-to-save-user-settings-using-userdefaults

2. Keychain

Userdefault is not secure. An access token is a sensitive information which should be stored in a secure place. So storing the access token in user default is not the correct choice. You must store access token in the keychain. Use SwiftKeychainWrapper pod to store token in Keychain.

    let tokenIdentifier = "TokenIdentifier"
    func storeAccessToken(token: String) {
        KeychainWrapper.standard.set(token, forKey: tokenIdentifier)
    }

    func checkUserLogin() {
        let token: String? = KeychainWrapper.standard.string(forKey: tokenIdentifier)
        if token != nil {
            print("User is Login")
        }
        else {
            print("User need to login")
        }
    }

Upvotes: 2

Related Questions