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.

1 answer

  • answered 2018-01-14 10:55 Hitesh Agarwal

    Use UserDefault for store token in memory. When app launch check if token is store in userdafault. UserDefault is use as short memory storage where you can store some 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

    Updated

    Userdefault is not secure. You can store access token in 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")
            }
        }