OAuth2, Swift 3, Instagram

2019-01-17 13:20发布

There seem to be lots of changes at IG. Many OAuth2 repos, all seem to have bugs, or really not easily converted to Swift3. Wondering if anyone has a solution for moving to Swift3 and working with the latest changes at Instagram?

Solutions most welcome. OAuth2 implementation seems to one of the more complicated things out there. Surprised that IG has not offered their own example docs on how to do this with iOS. They only have docs for web based solutions.

Maybe something brewing there? Zillions of coders they have on staff. But for now, on the hunt for a (dare I say?) simple solution.

thanks a million. :-)

2条回答
我想做一个坏孩纸
2楼-- · 2019-01-17 14:01

This Code bellow I use for facebook and google+, I think it'ill work for Instagram too, maybe some adjusts.

import UIKit

class Signup: UIViewController, UIWebViewDelegate {
    let GOOGLE_ID = "xxxxxx.apps.googleusercontent.com"
    let GOOGLE_SECRET = "xxxxxxx";
    let GOOGLE_REDIRECT_URI="http://yourdomain.com/api/account/googlecallback"
    let GOOGLE_TOKEN_URL = "https://accounts.google.com/o/oauth2/token";
    let GOOGLE_OAUTH_URL = "https://accounts.google.com/o/oauth2/auth";
    let GOOGLE_OAUTH_SCOPE = "profile email";
    let GOOGLE_GET_PROFILE = "https://www.googleapis.com/userinfo/v2/me";
    let FACEBOOK_ID = "xxxxx";
    let FACEBOOK_REDIRECT_URI = "http://yourdomain.com/api/account/facebookcallback";
    let FACEBOOK_OAUTH_URL = "https://www.facebook.com/dialog/oauth?client_id=";
    let FACEBOOK_OAUTH_SCOPE = "public_profile,email"
    let FACEBOOK_GET_PROFILE = "https://graph.facebook.com/me?access_token="
    var currentURL: String = ""
    var queryString: String = ""
    var receivedToken: String = ""
    var authCode: String = ""
    var authComplete = false
    var webV:UIWebView = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
    @IBAction func google(_ sender: AnyObject) {
        AppVars.Provider = "Google"
        webV.delegate = self
        let url = GOOGLE_OAUTH_URL + "?redirect_uri=" + GOOGLE_REDIRECT_URI + "&response_type=code&client_id=" + GOOGLE_ID + "&scope=" + GOOGLE_OAUTH_SCOPE
        let urlString :String =  url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
        webV.loadRequest(URLRequest(url: URL(string:urlString)!))
        self.view.addSubview(webV)
    }

    @IBAction func facebook(_ sender: AnyObject) {
        AppVars.Provider = "Facebook"
        webV.delegate = self
        let url = FACEBOOK_OAUTH_URL + FACEBOOK_ID + "&redirect_uri=" + FACEBOOK_REDIRECT_URI + "&scope=" + FACEBOOK_OAUTH_SCOPE + "&display=popup&response_type=token"
        let urlString :String =  url.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
        webV.loadRequest(URLRequest(url: URL(string:urlString)!))
        self.view.addSubview(webV)
    }
    func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
         self.showAlert(self, message: "Internet is not working")
    }

    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        return true;
    }

    func webViewDidStartLoad(_ webView: UIWebView) {
    }

    func webViewDidFinishLoad(_ webView: UIWebView) {
        currentURL = (webView.request?.url!.absoluteString)!
        if AppVars.Provider == "Google" {
           googleSignup((webView.request?.url!)!)
        } else {
           facebookSignup((webView.request?.url!)!)
        }
    }

    func googleSignup (_ returnCode: URL) {
            let url = String(currentURL)
            if (url?.range(of: "?code=") != nil && authComplete != true) {
                authCode =  getQueryItemValueForKey("code", url: returnCode)!
                authComplete = true
                let paramString = "code=" + authCode + "&client_id=" + GOOGLE_ID + "&client_secret=" +
                    GOOGLE_SECRET + "&redirect_uri=" + GOOGLE_REDIRECT_URI + "&grant_type=authorization_code"
                self.requestServer(urlSource: GOOGLE_TOKEN_URL, params: paramString, requestType: "POST") { (dataResult, errorResult) -> () in
                    if errorResult != nil {
                         self.showAlert(self, message: "Internet is not working")
                    } else {
                        let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
                        let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
                        do {
                            let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments)  as! [String:Any]
                            if let token = jsonDict["access_token"] as? String {
                                self.requestServerSignup(self.GOOGLE_GET_PROFILE, param: token, requestType: "GET") { (dataResult, errorResult) -> () in
                                    if errorResult != nil {
                                         self.showAlert(self, message: "Internet is not working")
                                    } else {
                                        let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
                                        let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
                                        do {
                                            let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments) as! [String:Any]
                                            if let name = jsonDict["name"] as? String {
                                                AppVars.NameLogin = name
                                                AppVars.PictureLogin = jsonDict["picture"] as! String
                                                AppVars.EmailLogin = jsonDict["email"] as! String
                                                self.performSegue(withIdentifier: "externalLoginSegue", sender: self)
                                                // show picture, email and name for checking profile
                                            }
                                        } catch  {
                                             self.showAlert(self, message: "Internet is not working")
                                        }
                                    }
                                }
                            }

                        } catch  {
                             self.showAlert(self, message: "Internet is not working")
                        }
                    }
                }
                self.webV.removeFromSuperview()
            }

    }

    func facebookSignup(_ returnCode: URL) {
            let url = String(currentURL)
            if (url?.range(of: "access_token=") != nil && authComplete != true) {
                let url2: String = returnCode.absoluteString.replacingOccurrences(of: "#access_token", with: "access_token")
                let url3: URL = URL(string: url2)!
                authCode =  getQueryItemValueForKey("access_token", url: (url3))!
                authComplete = true
                let paramString = "code=" + authCode + "&client_id=" + GOOGLE_ID + "&client_secret=" +
                    GOOGLE_SECRET + "&redirect_uri=" + GOOGLE_REDIRECT_URI + "&grant_type=authorization_code"
                self.requestServer(urlSource: self.FACEBOOK_GET_PROFILE + authCode + "&fields=name,picture,email", params: paramString, requestType: "GET") { (dataResult, errorResult) -> () in
                    if errorResult != nil {
                         self.showAlert(self, message: "Internet is not working")
                    } else {
                        let dataString:NSString = NSString(data: dataResult as! Data, encoding: String.Encoding.utf8.rawValue)!
                        let dataResult2 = dataString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)!
                        do {
                            let jsonDict = try JSONSerialization.jsonObject(with: dataResult2, options: .allowFragments) as! [String:Any]
                            if let name = jsonDict["name"] as? String {
                                AppVars.NameLogin = name
                                if let picture = jsonDict["picture"] as? [String:Any] {
                                    if let dataPicture = picture["data"] as? [String:Any] {
                                        if let url = dataPicture["url"] as? String {
                                            AppVars.PictureLogin = url
                                        }
                                    }
                                }
                                AppVars.EmailLogin = jsonDict["email"] as! String
                                self.performSegue(withIdentifier: "externalLoginSegue", sender: self)
                                // show picture, email and name for checking profile
                            }

                        } catch  {
                             self.showAlert(self, message: "Internet is not working")
                        }
                    }
                }
                self.webV.removeFromSuperview()
            }

    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }


    func getQueryItemValueForKey(_ key: String, url: URL) -> String? {
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
            return nil
        }
        guard let queryItems = components.queryItems else { return nil }
        return queryItems.filter {
            $0.name == key
            }.first?.value
    }

   func requestServer(urlSource:String, params:String, requestType:String, result:@escaping (_ dataResult:NSData?, _ errorResult:NSError?) -> ()) {
        let url: URL = URL(string: urlSource)!
        var request = URLRequest(url:url)
        request.httpMethod = requestType
        request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
        if params.characters.count > 0 {
            request.httpBody = params.data(using: String.Encoding.utf8)
        }
        let session = URLSession.shared
        session.dataTask(with: request) { (data, response, error) -> Void in
            DispatchQueue.main.async(execute: { () -> Void in
                if error == nil {
                    result(data as NSData?, nil)
                } else {
                    result(nil, error as NSError?)
                }
            })
        }.resume()
    }

}
查看更多
对你真心纯属浪费
3楼-- · 2019-01-17 14:05

For Swift 3:

Update: April 17 2017: Because of broken dependencies, the installation by Pods is no longer working. Therefore I have ripped the needed content and created a new Github project using a Bridging Header and stored all the needed files within the project. If you clone or download the github project, you'll instantly be able to login to Instagram.

To use those files in your project, simply drag and drop all the files from the SimpleAuth folder to your project, make sure to mark copy item if needed

enter image description here

Also you need to disable Disable implicit oAuth within the Instagram developer console.

enter image description here

Then you either copy/paste my code from the Bridging Header into your or you use mine. Set the Bridging Header at the Target's Build Settings.

enter image description here

Everything else works as before:

I have a struct for the Instagram Account:

struct InstagramUser {

    var token: String = ""
    var uid: String = ""
    var bio: String = ""
    var followed_by: String = ""
    var follows: String = ""
    var media: String = ""
    var username: String = ""
    var image: String = ""
}

The function to receive the token:

typealias JSONDictionary = [String:Any]
var user: InstagramUser?
let INSTAGRAM_CLIENT_ID = "16ee14XXXXXXXXXXXXXXXXXXXXXXXXX"
let INSTAGRAM_REDIRECT_URI = "http://www.davidseek.com/just_a_made_up_dummy_url" //just important, that it matches your developer account uri at Instagram

extension ViewController {

    func connectInstagram() {

        let auth: NSMutableDictionary = ["client_id": INSTAGRAM_CLIENT_ID,
                                         SimpleAuthRedirectURIKey: INSTAGRAM_REDIRECT_URI]

        SimpleAuth.configuration()["instagram"] = auth            
        SimpleAuth.authorize("instagram", options: [:]) { (result: Any?, error: Error?) -> Void in

            if let result = result as? JSONDictionary  {

                var token = ""
                var uid = ""
                var bio = "" 
                var followed_by = ""
                var follows = ""
                var media = ""
                var username = ""
                var image = ""

                token = (result["credentials"] as! JSONDictionary)["token"] as! String
                uid = result["uid"] as! String

                if let extra = result["extra"] as? JSONDictionary,
                    let rawInfo = extra ["raw_info"] as? JSONDictionary,
                    let data = rawInfo["data"] as? JSONDictionary {

                    bio = data["bio"] as! String

                    if let counts = data["counts"] as? JSONDictionary {
                        followed_by = String(describing: counts["followed_by"]!)
                        follows = String(describing: counts["follows"]!)
                        media = String(describing: counts["media"]!)
                    }
                }

                if let userInfo = result["user_info"] as? JSONDictionary {
                    username = userInfo["username"] as! String
                    image = userInfo["image"] as! String
                }

                self.user = InstagramUser(token: token, uid: uid, bio: bio, followed_by: followed_by, follows: follows, media: media, username: username, image: image)


            } else {
                // this handles if user aborts or the API has a problem like server issue
                let alert = UIAlertController(title: "Error!", message: nil, preferredStyle: UIAlertControllerStyle.alert)
                alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
                self.present(alert, animated: true, completion: nil)
            }

            if error != nil {
                print("Error during SimpleAuth.authorize: \(error)")
            }
        }
    }
}

Instagram also says:

Important

Even though our access tokens do not specify an expiration time, your app should handle the case that either the user revokes access, or Instagram expires the token after some period of time. If the token is no longer valid, API responses will contain an “error_type=OAuthAccessTokenException”. In this case you will need to re-authenticate the user to obtain a new valid token. In other words: do not assume your access_token is valid forever.

So handle the case of receiving the OAuthAccessTokenException

查看更多
登录 后发表回答