Giter VIP home page Giter VIP logo

oauth2's Introduction

OAuth2

Build Status License

OAuth2 frameworks for macOS, iOS and tvOS written in Swift 5.

OAuth2 requires Xcode 12.4, the built framework can be used on OS X 10.15 or iOS 12 and later. Happy to accept pull requests, please see CONTRIBUTING.md

Swift Version

Since the Swift language is constantly evolving I have adopted a versioning scheme mirroring Swift versions: the framework version's first two digits are always the Swift version the library is compatible with, see releases. Code compatible with brand new Swift versions are to be found on a separate feature branch named appropriately.

Usage

To use OAuth2 in your own code, start with import OAuth2 in your source files.

In OAuth2 there are different kinds of flows. This library supports all of them, make sure you're using the correct one for your use-case and authorization server. A typical code grant flow is used for demo purposes below. The steps for other flows are mostly the same short of instantiating a different subclass and using different client settings.

Still not working? See site-specific peculiarities.

1. Instantiate OAuth2 with a Settings Dictionary

In this example you'll be building an iOS client to Github, so the code below will be somewhere in a view controller of yours, maybe the app delegate.

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)

See those redirect_uris? You can use the scheme you want, but you must a) declare the scheme you use in your Info.plist and b) register the very same URI on the authorization server you connect to.

Note that as of iOS 9, you should use Universal Links as your redirect URL, rather than a custom app scheme. This prevents others from re-using your URI scheme and intercept the authorization flow.
If you target iOS 12 and newer you should be using ASWebAuthenticationSession, which makes using your own local redirect scheme secure.

Want to avoid switching to Safari and pop up a SafariViewController or NSPanel? Set this:

oauth2.authConfig.authorizeEmbedded = true
oauth2.authConfig.authorizeContext = <# your UIViewController / NSWindow #>

Need to specify a separate refresh token URI? You can set the refresh_uri in the Settings Dictionary. If specified the library will refresh access tokens using the refresh_uri you specified, otherwise it will use the token_uri.

Need to debug? Use a .debug or even a .trace logger:

oauth2.logger = OAuth2DebugLogger(.trace)

For more see advanced settings below.

2. Let the Data Loader or Alamofire Take Over

Starting with version 3.0, there is an OAuth2DataLoader class that you can use to retrieve data from an API. It will automatically start authorization if needed and will ensure that this works even if you have multiple calls going on. For details on how to configure authorization see step 4 below, in this example we'll use "embedded" authorization, meaning we'll show a SFSafariViewController on iOS if the user needs to log in.

This wiki page has all you need to easily use OAuth2 with Alamofire instead.

let base = URL(string: "https://api.github.com")!
let url = base.appendingPathComponent("user")

var req = oauth2.request(forURL: url)
req.setValue("application/vnd.github.v3+json", forHTTPHeaderField: "Accept")

self.loader = OAuth2DataLoader(oauth2: oauth2)
loader.perform(request: req) { response in
    do {
        let dict = try response.responseJSON()
        DispatchQueue.main.async {
            // you have received `dict` JSON data!
        }
    }
    catch let error {
        DispatchQueue.main.async {
            // an error occurred
        }
    }
}

3. Make Sure You Intercept the Callback

When using the OS browser or the iOS 9+ Safari view controller, you will need to intercept the callback in your app delegate and let the OAuth2 instance handle the full URL:

func application(_ app: UIApplication,
              open url: URL,
               options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
    // you should probably first check if this is the callback being opened
    if <# check #> {
        // if your oauth2 instance lives somewhere else, adapt accordingly
        oauth2.handleRedirectURL(url)
    }
}

For iOS 13 make the callback in SceneDelegate.swift

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
	if let url = URLContexts.first?.url {
		AppDelegate.shared.oauth2?.handleRedirectURL(url)
	}
}

You’re all set!


If you want to dig deeper or do authorization yourself, here it goes:

4. Manually Authorize the User

By default the OS browser will be used for authorization if there is no access token present or in the keychain. Starting with iOS 12, ASWebAuthenticationSession will be used when enabling embedded authorization on iOS (previously, starting with iOS 9, SFSafariViewController was used instead).

To start authorization call authorize(params:callback:) or, to use embedded authorization, the convenience method authorizeEmbedded(from:callback:).

The login screen will only be presented if needed (see _Manually Performing Authorization below for details) and will automatically dismiss the login screen on success. See Advanced Settings for other options.

oauth2.authorize() { authParameters, error in
    if let params = authParameters {
        print("Authorized! Access token is in `oauth2.accessToken`")
        print("Authorized! Additional parameters: \(params)")
    }
    else {
        print("Authorization was canceled or went wrong: \(error)")   // error will not be nil
    }
}

// for embedded authorization you can simply use:
oauth2.authorizeEmbedded(from: <# presenting view controller / window #>) { ... }

// which is equivalent to:
oauth2.authConfig.authorizeEmbedded = true
oauth2.authConfig.authorizeContext = <# presenting view controller / window #>
oauth2.authorize() { ... }

Don't forget, when using the OS browser or the iOS 9+ Safari view controller, you will need to intercept the callback in your app delegate. This is shown under step 2 above.

See Manually Performing Authorization below for details on how to do this on the Mac.

5. Receive Callback

After everything completes the callback will be called, either with a non-nil authParameters dictionary (which may be empty!), or an error. The access and refresh tokens and its expiration dates will already have been extracted and are available as oauth2.accessToken and oauth2.refreshToken parameters. You only need to inspect the authParameters dictionary if you wish to extract additional information.

For advanced use outlined below, there is the afterAuthorizeOrFail block that you can use on your OAuth2 instance. The internalAfterAuthorizeOrFail closure is, as its name suggests, provided for internal purposes – it is exposed for subclassing and compilation reasons and you should not mess with it. As of version 3.0.2, you can no longer use the onAuthorize and onFailure callback properties, they have been removed entirely.

6. Make Requests

You can now obtain an OAuth2Request, which is an already signed MutableURLRequest, to retrieve data from your server. This request sets the Authorization header using the access token like so: Authorization: Bearer {your access token}.

let req = oauth2.request(forURL: <# resource URL #>)
// set up your request, e.g. `req.HTTPMethod = "POST"`
let task = oauth2.session.dataTaskWithRequest(req) { data, response, error in
    if let error = error {
        // something went wrong, check the error
    }
    else {
        // check the response and the data
        // you have just received data with an OAuth2-signed request!
    }
}
task.resume()

Of course you can use your own URLSession with these requests, you don't have to use oauth2.session; use OAuth2DataLoader, as shown in step 2, or hand it over to Alamofire. Here's all you need to easily use OAuth2 with Alamofire.

7. Cancel Authorization

You can cancel an ongoing authorization any time by calling oauth2.abortAuthorization(). This will cancel ongoing requests (like a code exchange request) or call the callback while you're waiting for a user to login on a webpage. The latter will dismiss embedded login screens or redirect the user back to the app.

8. Re-Authorize

It is safe to always call oauth2.authorize() before performing a request. You can also perform the authorization before the first request after your app became active again. Or you can always intercept 401s in your requests and call authorize again before re-attempting the request.

9. Logout

If you're storing tokens to the keychain, you can call forgetTokens() to throw them away.

However your user is likely still logged in to the website, so on the next authorize() call, the web view may appear and immediately disappear. When using the built-in web view on iOS 8, one can use the following snippet to throw away any cookies the app created. With the newer SFSafariViewController, or logins performed in the browser, it's probably best to directly open the logout page so the user sees the logout happen.

let storage = HTTPCookieStorage.shared
storage.cookies?.forEach() { storage.deleteCookie($0) }

Manually Performing Authorization

The authorize(params:callback:) method will:

  1. Check if an authorize call is already running, if yes it will abort with an OAuth2Error.alreadyAuthorizing error
  2. Check if an access token that has not yet expired is already present (or in the keychain), if not
  3. Check if a refresh token is available, if found
  4. Try to use the refresh token to get a new access token, if it fails
  5. Start the OAuth2 dance by using the authConfig settings to determine how to display an authorize screen to the user

Your oauth2 instance will use an automatically created URLSession using an ephemeralSessionConfiguration() configuration for its requests, exposed on oauth2.session. You can set oauth2.sessionConfiguration to your own configuration, for example if you'd like to change timeout values. You can also set oauth2.sessionDelegate to your own session delegate if you like.

The wiki has the complete call graph of the authorize() method. If you do not wish this kind of automation, the manual steps to show and hide the authorize screens are:

Embedded iOS:

let url = try oauth2.authorizeURL(params: <# custom parameters or nil #>)
oauth2.authConfig.authorizeEmbeddedAutoDismiss = false
let web = try oauth2.authorizer.authorizeSafariEmbedded(from: <# view controller #>, at: url)
oauth2.afterAuthorizeOrFail = { authParameters, error in
    // inspect error or oauth2.accessToken / authParameters or do something else
    web.dismissViewControllerAnimated(true, completion: nil)
}

Modal Sheet on macOS:

let window = <# window to present from #>
let url = try oauth2.authorizeURL(params: <# custom parameters or nil #>)
let sheet = try oauth2.authorizer.authorizeEmbedded(from: window, at: url)
oauth2.afterAuthorizeOrFail = { authParameters, error in
    // inspect error or oauth2.accessToken / authParameters or do something else
    window.endSheet(sheet)
}

New window on macOS:

let url = try oauth2.authorizeURL(params: <# custom parameters or nil #>)
let windowController = try oauth2.authorizer.authorizeInNewWindow(at: url)
oauth2.afterAuthorizeOrFail = { authParameters, error in
    // inspect error or oauth2.accessToken / authParameters or do something else
    windowController.window?.close()
}

iOS/macOS browser:

let url = try oauth2.authorizeURL(params: <# custom parameters or nil #>)
try oauth2.authorizer.openAuthorizeURLInBrowser(url)
oauth2.afterAuthorizeOrFail = { authParameters, error in
    // inspect error or oauth2.accessToken / authParameters or do something else
}

macOS

See the OAuth2 Sample App's AppDelegate class on how to receive the callback URL in your Mac app. If the authorization displays the code to the user, e.g. with Google's urn:ietf:wg:oauth:2.0:oob callback URL, you can retrieve the code from the user's pasteboard and continue authorization with:

let pboard = NSPasteboard.general()
if let pasted = pboard.string(forType: NSPasteboardTypeString) {
    oauth2.exchangeCodeForToken(pasted)
}

Flows

Based on which OAuth2 flow that you need you will want to use the correct subclass. For a very nice explanation of OAuth's basics: The OAuth Bible.

Code Grant

For a full OAuth 2 code grant flow (response_type=code) you want to use the OAuth2CodeGrant class. This flow is typically used by applications that can guard their secrets, like server-side apps, and not in distributed binaries. In case an application cannot guard its secret, such as a distributed iOS app, you would use the implicit grant or, in some cases, still a code grant but omitting the client secret. It has however become common practice to still use code grants from mobile devices, including a client secret.

This class fully supports those flows, it automatically creates a “Basic” Authorization header if the client has a non-nil client secret. This means that you likely must specify client_secret in your settings; if there is none (like for Reddit) specify the empty string. If the site requires client credentials in the request body, set clientConfig.secretInBody to true, as explained below.

Implicit Grant

An implicit grant (response_type=token) is suitable for apps that are not capable of guarding their secret, such as distributed binaries or client-side web apps. Use the OAuth2ImplicitGrant class to receive a token and perform requests.

Would be nice to add another code example here, but it's pretty much the same as for the code grant.

Client Credentials

A 2-legged flow that lets an app authorize itself via its client id and secret. Instantiate OAuth2ClientCredentials, as usual supplying client_id but also a client_secret – plus your other configurations – in the settings dict, and you should be good to go.

Username and Password

The Resource Owner Password Credentials Grant is supported with the OAuth2PasswordGrant subclass. Create an instance as shown above, set its username and password properties, then call authorize().

Device Grant

The OAuth 2.0 Device Authorization Grant flow is implemented in the OAuth2DeviceGrant subclass. Although this flow is designed for devices that either lack a browser to perform a user-agent-based authorization or are input constrained, it is also very useful for applications not allowed to start their own webserver (loopback URL) or register a custom URL scheme to finish the authorization code grant flow. To initiate the device grant flow, the deviceAuthorizeURL needs to be correctly configured to point towards the device authorization endpoint. By calling the OAuth2DeviceGrant.start(useNonTextualTransmission:params:completion:) method, the client obtains all necessary details to complete the authorization on a secondary device or in the system browser.

Site-Specific Peculiarities

Some sites might not strictly adhere to the OAuth2 flow, from returning data differently like Facebook to omitting mandatory return parameters like Instagram & co. The framework deals with those deviations by creating site-specific subclasses and/or configuration details. If you need to pass additional headers or parameters, you can supply these in the settings dict like so:

let oauth2 = OAuth2CodeGrant(settings: [
    "client_id": "...",
    ...
    "headers": ["Accept": "application/vnd.github.v3+json"],
    "parameters": ["duration": "permanent"],
] as OAuth2JSON)

Advanced Settings

The main configuration you'll use with oauth2.authConfig is whether or not to use an embedded login:

oauth2.authConfig.authorizeEmbedded = true

Similarly, if you want to take care of dismissing the login screen yourself (not possible with the newer authorization sessions mentioned below):

oauth2.authConfig.authorizeEmbeddedAutoDismiss = false

Some sites also want the client-id/secret combination in the request body, not in the Authorization header:

oauth2.clientConfig.secretInBody = true
// or in your settings:
"secret_in_body": true

Sometimes you also need to provide additional authorization parameters. This can be done in 3 ways:

oauth2.authParameters = ["duration": "permanent"]
// or in your settings:
"parameters": ["duration": "permanent"]
// or when you authorize manually:
oauth2.authorize(params: ["duration": "permanent"]) { ... }

Similar is how you specify custom HTTP headers:

oauth2.clientConfig.authHeaders = ["Accept": "application/json, text/plain"]
// or in your settings:
"headers": ["Accept": "application/json, text/plain"]

Some sites (e.g. Slack) validate the User-Agent string against supported browser versions, which may not match WebKit's default in embedded mode. The embedded mode User-Agent may be overriden with:

oauth2.customUserAgent = "Version/15.6.1 Safari"
// or in your settings:
"custom_user_agent": "Your string of choice"

Starting with version 2.0.1 on iOS 9, SFSafariViewController will be used for embedded authorization. Starting after version 4.2, on iOS 11 (SFAuthenticationSession) and iOS 12 (ASWebAuthenticationSession), you can opt-in to these newer authorization session view controllers:

oauth2.authConfig.ui.useAuthenticationSession = true

To revert to the old custom OAuth2WebViewController, which you should not do because ASWebAuthenticationSession is way more secure:

oauth2.authConfig.ui.useSafariView = false

To customize the go back button when using OAuth2WebViewController on iOS 8 and older:

oauth2.authConfig.ui.backButton = <# UIBarButtonItem(...) #>

See below for settings about the keychain and PKCE.

Usage with Alamofire

You'll get the best experience when using Alamofire v4 or newer and OAuth2 v3 and newer:

Dynamic Client Registration

There is support for dynamic client registration. If during setup registration_url is set but client_id is not, the authorize() call automatically attempts to register the client before continuing to the actual authorization. Client credentials returned from registration are stored to the keychain.

The OAuth2DynReg class is responsible for handling client registration. You can use its register(client:callback:) method manually if you need to. Registration parameters are taken from the client's configuration.

let oauth2 = OAuth2...()
oauth2.registerClientIfNeeded() { error in
    if let error = error {
        // registration failed
    }
    else {
        // client was registered
    }
}
let oauth2 = OAuth2...()
let dynreg = OAuth2DynReg()
dynreg.register(client: oauth2) { params, error in
    if let error = error {
        // registration failed
    }
    else {
        // client was registered with `params`
    }
}

PKCE

PKCE support is controlled by the useProofKeyForCodeExchange property, and the use_pkce key in the settings dictionary. It is disabled by default. When enabled, a new code verifier string is generated for every authorization request.

Keychain

This framework can transparently use the iOS and macOS keychain. It is controlled by the useKeychain property, which can be disabled during initialization with the keychain settings dictionary key. Since this is enabled by default, if you do not turn it off during initialization, the keychain will be queried for tokens and client credentials related to the authorization URL. If you turn it off after initialization, the keychain will be queried for existing tokens, but new tokens will not be written to the keychain.

If you want to delete the tokens from keychain, i.e. log the user out completely, call forgetTokens(). If you have dynamically registered your client and want to start anew, you can call forgetClient().

Ideally, access tokens get delivered with an "expires_in" parameter that tells you how long the token is valid. If it is missing the framework will still use those tokens if one is found in the keychain and not re-perform the OAuth dance. You will need to intercept 401s and re-authorize if an access token has expired but the framework has still pulled it from the keychain. This behavior can be turned off by supplying token_assume_unexpired: false in settings or setting clientConfig.accessTokenAssumeUnexpired to false.

These are the settings dictionary keys you can use for more control:

  • keychain: a bool on whether to use keychain or not, true by default
  • keychain_access_mode: a string value for keychain kSecAttrAccessible attribute, "kSecAttrAccessibleWhenUnlocked" by default, you can change this to e.g. "kSecAttrAccessibleAfterFirstUnlock" if you need the tokens to be available when the phone is locked.
  • keychain_access_group: a string value for keychain kSecAttrAccessGroup attribute, nil by default
  • keychain_account_for_client_credentials: the name to use to identify client credentials in the keychain, "clientCredentials" by default
  • keychain_account_for_tokens: the name to use to identify the tokens in the keychain, "currentTokens" by default

Installation

You can use the Swift Package Manager, git or Carthage. The preferred way is to use the Swift Package Manager.

Swift Package Manager

In Xcode 11 and newer, choose "File" from the Xcode Menu, then "Swift Packages" » "Add Package Dependency..." and paste the URL of this repo: https://github.com/p2/OAuth2.git. Pick a version and Xcode should do the rest.

Carthage

Installation via Carthage is easy enough:

github "p2/OAuth2" ~> 4.2

git

Using Terminal.app, clone the OAuth2 repository, best into a subdirectory of your app project:

$ cd path/to/your/app
$ git clone --recursive https://github.com/p2/OAuth2.git

If you're using git you'll want to add it as a submodule. Once cloning completes, open your app project in Xcode and add OAuth2.xcodeproj to your app:

Adding to Xcode

Now link the framework to your app:

Linking

These three steps are needed to:

  1. Make your App also build the framework
  2. Link the framework into your app
  3. Embed the framework in your app when distributing

License

This code is released under the Apache 2.0 license, which means that you can use it in open as well as closed source projects. Since there is no NOTICE file there is nothing that you have to include in your product.

oauth2's People

Contributors

aidzz avatar amaurydavid avatar andrewschenk avatar blork avatar cgossain avatar davidkraus avatar ddengler avatar dhardiman avatar djbe avatar dominikpalo avatar drdavec avatar glennschmidt avatar hoereth avatar insidegui avatar lindemann avatar lubbo avatar m4p avatar maximelm avatar ossus-lib avatar p2 avatar paulw11 avatar pwrzesinski avatar raudabaugh avatar robertbarclay avatar sebskuse avatar spectraldragon avatar tiagomnh avatar tschmitz avatar tylerswartz avatar vojto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oauth2's Issues

how to use hasUnexpiredAccessToken() to avoid login again and again

I have done like this

  if(appDelegate.oauth.hasUnexpiredAccessToken()){

        println("Un-Expired Access Token")

        self.navigationController?.pushViewController(self.PersonalDriverUber_VC, animated: true)

    }else{

        println("Expired Access Token")

        appDelegate.oauth.onAuthorize = { parameters in
            println("Did authorize with parameters: \(parameters)")

            self.personalDriverLoader.stopAnimating()

            self.navigationController?.pushViewController(self.PersonalDriverUber_VC, animated: true)

        }
        appDelegate.oauth.onFailure = { error in        // `error` is nil on cancel
            if nil != error {
                println("Authorization went wrong: \(error!.localizedDescription)")
            }
        }


    }

at first time launch I am getting

Did authorize with parameters: [token_type: Bearer, expires_in: 2592000, access_token: laerXXXXX2NcXXXXXXXJc3mtT2t, refresh_token: DqWXXXXXXMBaKKFdPXXXXkZv, scope: request history, last_authenticated: 1431947251]
note: expires_in: 2592000 in the reponse

I want to know how to use the method hasUnexpiredAccessToken() to avoid login screen again and again. or there is any way to refresh the token

See my above code every time my ELSE block executes

obtain soundcloud token

I wanted to implement this lib to use the soundcloud SDK but somehow i do not get a valid token back:

my settings:

var oauth2 = OAuth2CodeGrant(settings: [
        "client_id": "xxx",
        "client_secret": "xxx",
        "authorize_uri": "https://soundcloud.com/connect",
        "token_uri": "https://api.soundcloud.com/oauth2/token",
        "scope": "non-expiring",
        "redirect_uris": ["myApp://oauth2"],  
        "keychain": false, 
        "verbose":true,
        "secret_in_body":true //soundcloud requests everything in body
        ])

These are the last lines of the logfile

OAuth2: Authorizing against https://api.soundcloud.com/oauth2/token?grant_type=authorization_code&client_secret=xxx&scope=non-expiring&code=xxx&client_id=xxx&redirect_uri=xxx&state=xxx
OAuth2: Exchanging code xxx with redirect xxx for access token at https://api.soundcloud.com/oauth2/token
OAuth2: Did not get access token expiration interval
OAuth2: Did exchange code for access [true] and refresh [false] tokens
OAuth2: Storing tokens to keychain
(Function)
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (No value.) UserInfo=0x7fe3659573f0 {NSDebugDescription=No value.}

SoundCloud API reference can be found here: https://developers.soundcloud.com/docs/api/reference#connect

Logout - oauth2.forgetTokens() doesn't fully remove tokens

With the embedded web view, oauth2.forgetTokens() doesn't seem to clear everything out.

Tried setting keychain to false.

2nd call to oauth2.authorizeEmbeddedFrom(vc, params: nil) immediately returns success with the tokens, but I would like it to force the user to log in again.

Browser caching or something?

Thanks!

Bad credentials, but have valid token

Hi again!

I am getting an access token, but my requests are resulting in "Bad credentials"
Did authorize with parameters: {
"access_token" = XXX...xje3x3EGww4lTIlTZYEgfb2saUOtsgsSip5DENe;
"token_type" = Bearer;
}

Here is my request method (i did try to manually set the Authorization header):

func request(path: String, callback: ((dict: NSDictionary?, error: NSError?) -> Void)) {
        let url = baseURL.URLByAppendingPathComponent(path)
        let req = oauth2.request(forURL: url)

        req.setValue("application/vnd.github.v3+json", forHTTPHeaderField: "Accept")
//        req.setValue("Bearer \(oauth2.accessToken)", forHTTPHeaderField: "Authorization")

        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithRequest(req) { data, response, error in
            if nil != error {
                dispatch_async(dispatch_get_main_queue(), {
                    callback(dict: nil, error: error)
                })
            }
            else {
                var err: NSError?
                let dict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &err) as? NSDictionary
                dispatch_async(dispatch_get_main_queue(), {
                    callback(dict: dict, error: err)
                })
            }
        }
        task.resume()
    }

Any ideas?

Slow UI View Updates

I am facing strange issue

I have called the method like this

    func callRequestAPI(url:String){

    var configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    var session = NSURLSession(configuration: configuration)

    let params:[String: AnyObject] = [
        "product_id" : selectedUberProductId,
        "start_latitude" : start_lat,
        "start_longitude" : start_lng,
        "end_latitude" : end_lat,
        "end_longitude" : end_lng]

    let request = appDelegate.oauth.request(forURL: NSURL(string:url)!)
    request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
    request.HTTPMethod = "POST"
    var err: NSError?
    request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.allZeros, error: &err)

    let task = session.dataTaskWithRequest(request) {
        data, response, error in

        if let httpResponse = response as? NSHTTPURLResponse {
            if httpResponse.statusCode != 202 {
                println("response was not 202: \(response)")

                return
            }
        }
        if (error != nil) {
            println("error submitting request: \(error)")
            return
        }

        // handle the data of the successful response here
        var result = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: nil) as! NSDictionary


        println(result)

        if let request_id: String = result["request_id"] as? String{

            println(request_id) // This is getting printed very frequently
            self.uber_request_id.text = "Request ID: \(request_id)" // uber_request_id (Lable View) not Update Frequently it takes Approx 1 minute to update

        }

        if let driver: String = result["driver"] as? String{

            println(driver)
        }

        if let eta: Int = result["eta"] as? Int{

            println(eta) //This is getting printed very frequently
            self.uber_request_eta.text = String("ETA: \(eta)") //My Lable not Update Frequently
        }

        if let location: String = result["location"] as? String{

            println(location)
        }


        if let status: String = result["status"] as? String{

            println(status)// This is getting printed very frequently
            self.uber_request_status.text = "Status: \(status)" // My Lable not Update Frequently
        }


        if let surge_multiplier: Int = result["surge_multiplier"] as? Int{

            println(surge_multiplier)
        }

        if let vehicle: String = result["vehicle"] as? String{

            println(vehicle)
        }

    }

    task.resume()
}

Note: See the commented Lables
I am sure im getting the response very frequetly but my Labels are not getting updated ( it takes Approx 1 minute to update my views)
Whats Wrong with this

Please help

Safari "Cannot Open Page" upon GitHub authentication in iOS 8

Hi.
Thanks for providing this library. Im very close to getting this to work...
I'm using your GitHub example in iOS and upon authenticating I get "Cannot Open Page" in Safari.
I have properly registered the URL and if I paste it into Safari it opens the app correctly.
My redirect URI is set:
"redirect_uris": ["grid://oauth/callback"],

Im using the latest Xcode 6.1.1 and your master branch (not the Xcode 6.1 tag) but I do not get any build errors so I do not think that is the issue.

Any idea why Safari is stalling?

screenshot 2015-01-08 02 27 40
screenshot 2015-01-08 02 29 10

No redirection after Google+ authentication

Performed a OAuth2CodeGrant operation with Google+ settings:

let settings = [
    "client_id": xxxx,
    "authorize_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://accounts.google.com/o/oauth2/token",
    "scope": "https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email",
    "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob:auto"],
]

The authentication succeeded by the OAuth2WebView was not dismissed automatically.

I put a breakpoint in shouldStartLoadWithRequest method of the OAuth2WebViewController and noticed that request.URL.scheme does not equal to interceptComponents?.scheme and the same for request.URL.host and interceptComponents?.host. So onInterceptcallback is never called.

Implementation with Alamofire

Hello!

Is there a plan to implement p2.oauth2 in Alamofire in someway ?

I think it could be nice, so you don't need to set the tokens when you make a request.

Please clarify how to install in documentation

Hi, I'm having trouble adding your library to my project. The documentation states "the built framework can be used on ... iOS 8 and later." What specifically do I add tp my project?

I've downloaded the code and opened it Xcode and I see a folder named Products with two references to Oauth2.framework, both in red, so no files found. Do I need to build something?

I've also looked at your sample app in Xcode, and I see there a reference to OAuth2.xcodeproj, but as a folder. If I add that file to my project, it stays a file. Even if I drag the folder from the sample app it turns into a file. In either case, when I navigate to Build Phases > Target Dependencies I don't see Oauth2 as it is in the sample app.

I'm relatively new to iOS/Xcode, so hopefully I'm not missing something obvious. I've googled to no avail.

Thanks!

Side effect with SafariViewController upon logout.

Hello.

Some time ago we discussed (#25) a way to clear cookies in the web view in a "log out" scenario.
This was the snippet that handled that:

let storage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
        for cookie in storage.cookies! {
            storage.deleteCookie(cookie)
        }
        NSUserDefaults.standardUserDefaults()

Now with SafariViewController this approach won't work, and I understand why.

My question is do you have any idea of another approach to address the issue?

Error during JSON serialization with Facebook authentication

Performed a OAuth2CodeGrant operation with Facebook settings:

let settings = [
            "client_id": "xxxx",
            "client_secret": "xxxx",
            "authorize_uri": "https://graph.facebook.com/oauth/authorize",
            "token_uri": "https://graph.facebook.com/oauth/access_token",
            "scope": "email",
            "redirect_uris": ["domain name"],
        ]

And got the error Authorization went wrong: The operation couldn’t be completed. (Cocoa error 3840.) after the JSONObjectWithData method has been called:

let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &finalError) as? NSDictionary

Problems with paramsFromQuery

In OAuth2Base i have some problems with

public final class func paramsFromQuery(query: String) -> [String: String] {
        let parts = split(query.characters) { $0 == "&" }.map() { String($0) }
        var params = [String: String](minimumCapacity: parts.count)
        for part in parts {
            let subparts = split(part.characters) { $0 == "=" }.map() { String($0) }
            if 2 == subparts.count {
                params[subparts[0]] = subparts[1].wwwFormURLDecodedString
            }
        }

        return params
    }

It says following

p2.OAuth2/OAuth2/OAuth2Base.swift:203:15: 'split(_:maxSplit:allowEmptySlices:isSeparator:)' is unavailable: Use the split() method instead.

Any idea what is is?

Using the latest Xcode beta and iOS 9 beta on OS X 10.10.4

afterAuthorizeOrFailure not called on failure

I have a case where if my authentication server is not responding, afterAuthorizeOrFailure never calls the callback.

    typealias SuccessResponse = (Bool) -> Void
    typealias OAuth2Response = (wasFailure: Bool, error: NSError?) -> Void

    private func internalAuthorize(oauth2: OAuth2PasswordGrant?, callback: OAuth2Response) {

        if let oauth2 = self.oauth2 {

            oauth2.afterAuthorizeOrFailure = callback

            oauth2.authorize(params: nil, autoDismiss: true)
        } else {
            callback(wasFailure: true, error: nil)
        }
    }

    func authorize(email: String, password: String, callback: SuccessResponse) {

        let settings = [
            "client_id": OAuth.ClientId,
            "client_secret": OAuth.ClientSecret,
            "authorize_uri": OAuth.AuthorizeURI,
            "token_uri": OAuth.TokenURI,
            "scope": "",
            "redirect_uris": ["myapp://oauth/callback"],   // don't forget to register this scheme
            "keychain": true,
            "username": email,
            "password": password
        ] as OAuth2JSON

        oauth2?.forgetTokens() // We must explicitly call this to avoid data hanging around

        self.oauth2 = OAuth2PasswordGrant(settings: settings)

        if email != "facebook" {
            keychain["email"] = email
            keychain["password"] = password
            keychain["lastAuthentication"] = "email"
        }

        self.internalAuthorize(self.oauth2) { (wasFailure, error) -> Void in
            log.debug("\(wasFailure)") // This never happens
            callback(!wasFailure)
        }

        // Update device token for push notifications
        UserService().updateMyDevice(PFInstallation.currentInstallation().deviceToken, callback: { (success) -> Void in
            print(success)
        })
    }

This is probably a "me" error, but I'm not sure.

How to use the built-in web view controller

as Docs suggested,
either use the built-in web view controller or manually open the authorize URL in the browser:

I am using built-in web view controller approach.

I am trying like this

    let vc   = UberAuthWebViewController(nibName: "UberAuthWebViewController", bundle: nil)
    let web = appDelegate.oauth.authorizeEmbeddedFrom(vc, params: nil)
    appDelegate.oauth.afterAuthorizeOrFailure = { wasFailure, error in

        println("Test afterAuthorizeOrFailure")
        web.dismissViewControllerAnimated(true, completion: nil)
    }

But I getting no crash but this warning and nothing open up.
Warning: Attempt to present UINavigationController: 0x166620a0 on jamesAppV2.UberMapViewController: 0x1716e200 whose view is not in the window hierarchy!

Here is my UberAuthWebViewController.swift class

class WebViewVC: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    } 

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

   }

may be I am commiting a mistake while making my WebViewController's Object.
Please help

oauth2.authorize(params: ["duration":"permanent"]) not added to auth url <REDDIT>

I am trying to get a reauth token from reddit however the initial authorization url being generated using the same code flow detailed here - https://github.com/p2/OAuth2App/blob/master/OAuth2App/RedditLoader.swift - shows the following in log:

OAuth2: Authorizing against https://www.reddit.com/api/v1/authorize.compact?client_id=XXXXXXXXXXX&redirect_uri=reddit%3A%2F%2Fresponse&scope=identity%2Cedit%2Cflair%2Chistory%2Cmysubreddits%2Cprivatemessages%2Cread%2Creport%2Csave%2Csubmit%2Csubscribe%2Cvote&response_type=code&state=66BEFFB7

which shows no &duration=permanent in the url.

I am calling oauth2.authorize(params: ["duration":"permanent"]) but that still does not do the trick. Without reauth token support, there is no way to keep a user logged in for more than an hour without a manual relogin.

OSX compatibility?

I wasn't able to compile this for OSX

OAuth2.h
error thrown for
@import Foundation
(missing semicolon)

OAuth2WebViewController.swift
error thrown for
import UIKit

Cancel an ongoing authorization request

Is there a way to cancel an ongoing authorize() request?

In the readme it says:

oauth2.onFailure = { error in        // `error` is nil on cancel
    if nil != error {
        print("Authorization went wrong: \(error!.localizedDescription)")
    }
}

But the doc doesn't mention you can actually cancel the request..?
Is it possible?

Archiving fails for OAuth2

Building and running works just fine, but trying to archive I get:

Command failed due to signal: Segmentation fault: 11

within the OAuth2iOS framework... any ideas?

refresh tokens?

Is it best to call oauth2.authorize() before any API calls are made to ensure that if the token has expired then it is refreshed?

puzzled how to use the refresh method?

NSURLSession.dataTaskWithRequest() returns empty/200 when no call made

NSURLSession.dataTaskWithRequest() is returning empty data and a 200 http code immediately, without actually making an outbound http call to the server using sample code in the readme. Adjusted the sample code as below and fixed it. Think it has something to do with async nature of this call?

    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(req) {
        (
        let data, let response, let error) in

        guard let _:NSData = data, let _:NSURLResponse = response  where error == nil else {
            print("error")
            return
        }

        let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
        print(dataString)

    }
    task.resume()

OAuth fails on WebView after SDK8.4 update

I got a weird problem since I updated to 8.4 SDK - on a live device running either 8.3. or 8.4 the OAuth will instantly fail on the WebView where you enter your credentials.

The Log:

OAuth2: Initialized with client id xxx
OAuth2: No access token, maybe I can refresh
OAuth2: I don't have a refresh token, not trying to refresh
OAuth2: Authorizing against https://soundcloud.com/connect?client_id=xxx&redirect_uri=xxxx%3A%2F%2Fsoundcloud-redirect&scope=non-expiring&response_type=code&state=4A31B29D
OAuth2: No access token, maybe I can refresh
OAuth2: I don't have a refresh token, not trying to refresh
OAuth2: Authorizing against https://soundcloud.com/connect?client_id=xxx&redirect_uri=xxx%3A%2F%2Fsoundcloud-redirect&scope=non-expiring&response_type=code&state=4A31B29D
fatal error: Cannot open authorize URL: file /Users/xxx/xxx/xxx/Pods/p2.OAuth2/OAuth2/OAuth2.swift, line 296

Somehow on a simulator, this error does not occur - it will do everything just fine.

String is not identifcal to NSObject

Following the Usage steps and I get:

eh

Also, minor, but there's no note about the need for import Oauth2

I'm new, perhaps I missed a step and am causing my own problems? Note: xCode 6.2 (6C131e)

NSURLComponents encode problem

I faced a problem that the value of the param redirect_uri needed to be precent encoded. However the part in OAuth2.swift where the URL is composed with NSURLComponents (line 195) does not percent encode a value like 'http://some_redirect' and so the server gave a 500 error.
I had to this:
comp!.percentEncodedQuery = OAuth2.queryStringFor(urlParams).stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
to make it work...

Unresolved identifier OAuth2CodeGrant

Hi - I am running XCode 7.01 and having trouble properly linking the p2.OAuth2 library to my project. Are the current versions of Swift and XCode supported?

Thanks

Checking if user is authorized without authorizing?

Maybe I am missing something but I dont see how to check if the user if authorized without starting the entire OAuth dance.

When my app is launched, I want to check to see if the user is authorized. If they are, I would use the access token like normal but if they ARE NOT I want to display a button that says "Sign In" which would THEN trigger the typical OAuth dance. This seems to be fairly standard for apps (i.e. when you start it up, if they are logged in just keep them logged in without requiring them to click sign in) but Im not sure how to do it.

Thanks.

OAuth2PasswordGrant should parse refresh token

Hi,

I'm using OAuth2PasswordGrant in my project and I don't think the "password" flow is fully implemented.

First, OAuth2PasswordGrant will try to use the authorization_uri instead of the token_uri to request and access_token using the username and password. (This can be hacked by using the a token uri as the authorization_uri value).

Second, I believe all grant types allow to refresh access_token using the refresh_token. I just see this implemented in OAuth2CodeGrant. I think all the refresh logic (refreshToken, doRefreshToken(), ...) should be shared with OAuth2PasswordGrant.

Cheers.

Handling Password Grant Type

I have a need to add a password grant type, I have the bulk of the work done, but was curious if you would be open to me adding it to the library?

Error with the new SFSafariViewController

When i use the smart and new SFSafariViewController i get and error: "Cannot Open Page - Safari cannot open the because the address is invalid" and when i press OK the site i blank.

Is there something i need to implement ??

// Kim

Swift 1.2 Update

You probably realise but theres a few compile errors after the latest Xcode update. somethings are a quick fix countElement is replaced with count.
but if not sure what to do about this one

OAuth2CodeGrant.swift:45:9:
Property 'self.tokenURL' not initialized at super.init call

Some websites (like reddit) require http basic authentication to allow tokenRequest

The function tokenRequest in OAuth2CodeGrant does not allow specification of Headers for http basic authentication.

As a result, when trying to setup OAuth2 with reddit, it fails.

As a hack, I added this to allow it to work. But obviously it needs to be a little smarter about passing around the user/pass rather than doing a hardcoding.

    let username = “username“ //Client ID in Reddit
    let password = “password” //Client Secret in Reddit
    let loginString = NSString(format: "%@:%@", username, password)
    let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
    var base64LoginString = "Basic "
    base64LoginString += loginData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
    post.setValue(base64LoginString,forHTTPHeaderField: "Authorization")

UITextField to Settings in Password Grant

Hi quick question,

I am currently using the Password Grant to authenticate my users. However, my 'username' and 'password' fields are dependent on my UITextFields. I am unable to set this as I will be getting a 'instance member cannot be used on type view controller'. How do I go about doing this? Thanks!

onFailure not getting called upon dismissal of SFSafariViewController

onAuthorize get's called appropriately, but if the user taps Done in SFSafariViewController onFailure does not get called.

    /** Start the OAuth dance. */
    public func requestToken(viewController vc: UIViewController, onAuthorize: ((accessToken: String?) -> Void)?, onFailure: ((error: NSError?) -> Void)?) {
        oauth2.authConfig.authorizeEmbedded = true
        oauth2.authConfig.authorizeContext = vc
        oauth2.authConfig.ui.backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)

        oauth2.onAuthorize = { parameters in
            onAuthorize?(accessToken: self.oauth2.accessToken)
        }
        oauth2.onFailure = { error in
            print(error) // does not fire
        }
        oauth2.authorize(params: nil, autoDismiss: true)
    }

Use of unresolved identifier Keychain

Hi,

Im trying to get your code working but run into the error mentioned in the title. Environment is XCode 7 (new beta)/Swift 2 (running of that branch). ANy ideas how to fix this?

Tnx

Missing expires_in causes access token to always appear expired

According to OAuth 2.0 RFC

expires_in
         RECOMMENDED.  The lifetime in seconds of the access token.  For
         example, the value "3600" denotes that the access token will
         expire in one hour from the time the response was generated.
         If omitted, the authorization server SHOULD provide the
         expiration time via other means or document the default value.

If expires_in is not present, each re-launch of the app will result in the error:

OAuth2: Found access token but it seems to have expired

Would it be possible to add an option to set a default expires in interval when one isn't provided by the access token response or treat the token as permanent?

Swift 1.2

Swift 1.2 support would be awesome!

bundle format is ambiguous (could be app or framework)

I have implement this library,
Well I must appreciate the Docs

I am facing an issue here
While Archiving im getting this

CodeSign /Users/shujaathaider/Library/Developer/Xcode/DerivedData/jamesAppV2-hhfnrydqoghbzpgsvepxtuzarqrc/Build/Intermediates/ArchiveIntermediates/Home\ Automation\ Sys\ Swift/IntermediateBuildFilesPath/UninstalledProducts/OAuth2.framework
cd "/Users/shujaathaider/Desktop/Workspace_qadir/JamesApp - Development/OAuth2-master"
export CODESIGN_ALLOCATE=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate
export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"

Signing Identity: "iPhone Developer: Ghulhimam Haanani (HC4WE7XBSD)"

/usr/bin/codesign --force --sign C72D0E8EEBE104D1EA0AA0D15558EF38808250F3 /Users/shujaathaider/Library/Developer/Xcode/DerivedData/jamesAppV2-hhfnrydqoghbzpgsvepxtuzarqrc/Build/Intermediates/ArchiveIntermediates/Home\ Automation\ Sys\ Swift/IntermediateBuildFilesPath/UninstalledProducts/OAuth2.framework

/Users/shujaathaider/Library/Developer/Xcode/DerivedData/jamesAppV2-hhfnrydqoghbzpgsvepxtuzarqrc/Build/Intermediates/ArchiveIntermediates/Home Automation Sys Swift/IntermediateBuildFilesPath/UninstalledProducts/OAuth2.framework: bundle format is ambiguous (could be app or framework)
Command /usr/bin/codesign failed with exit code 1

Please Help, I have to make an IPA of my App.

I have posted question on stackoverflow also

http://stackoverflow.com/questions/29946706/bundle-format-is-ambiguous-could-be-app-or-framework-while-making-archive

SFSafariViewController not implemented

I'm not too sure where to ask this (GitHub repositories don't really have feature request pages), but is it possible to add SFSafariViewController to this framework? Now that we can submit iOS 9 apps, I'm sure that it would be a much better user experience.

possible to initialize OAuth2 with token

after logging in once I've got a token, let's say I saved it somewhere safe is it possible after a reboot to initialize the object with the token and start doing requests with it immediately?

Renew access token from refresh token

How do I most easily implement so that I do not need to log in every time? I assume the framework supports so I can use hasUnexpiredAccessToken() to first check if accesstoken is ok, then if not, try to use refresh token to get a new unexpired access token.

What's the best way to do this?

Also do you recommend any way of storing the tokens on the device so they don't get cleared when closing the app?

Clear cookies?

If a user signs out of my app, and then click sign in via Uber, they are automatically logged in with the credentials of the pervious user. How do I make it so that it allows me to sign out?

Add CocoaPods support

CocoaPods recently added support for Swift. Could we add an Oauth2.podspec file to make this great lib available through CocoaPods?

PasswordGrant Questions

I am implemeting your framework in my App and I have some questions:

  1. Why it don't support "client_id & client_secret" in body?
  2. On authorization I receive this warning: "WARNING: expecting “bearer” token type but got “Bearer”", why? There are no difference between "Bearer" and "bearer", but it is not nice to see a useless warning when debug. Many OAuth providers use both variants.
  3. What mean "params" array and "autoDismiss" boolean? There are information in Docs. If they are useless, then why there are required? oauth2.authorize(params: nil, autoDismiss: true);
  4. Why it use for requesting "access_token" the "authorize_uri"? In Password Grant it must request directly the "token_uri".
  5. parameters in oauth2.onAuthorize returns empty string.
oauth2.onAuthorize = { parameters in
    print("Did authorize with parameters: \(parameters)")
}

Console: Did authorize with parameters: [:]

Not able to get access token of Linkedin

I am trying to get access token for Linkedin. As mentioned in the usage section, I have created the following code for getting access token.

  let settingsLinkedin = [
        "client_id": "75afkn125135v14",
        "client_secret": "R5gHlmkRtld4xge4",
        "authorize_uri": "https://www.linkedin.com/uas/oauth2/authorization",
        "token_uri": "https://www.linkedin.com/uas/oauth2/accessToken",
        "scope": "",
        "redirect_uris": ["http://www.dummyredirecturl.com/oauth.aspx"],   // don't forget to register this scheme
        "keychain": false        ] as OAuth2JSON            // the "as" part may or may not be needed


        let oauth2 = OAuth2CodeGrant(settings: settingsLinkedin)
        oauth2.onAuthorize = { parameters in
            println("Did authorize with parameters: \(parameters)")
        }
        oauth2.onFailure = { error in        // `error` is nil on cancel
            if nil != error {
                println("Authorization went wrong: \(error!.localizedDescription)")
            }
        }


        oauth2.authConfig.authorizeEmbedded = true
        oauth2.authConfig.authorizeContext = self.view.window!.rootViewController
        //oauth2.authConfig.ui.backButton = <# UIBarButtonItem(...) #>    // to customize go-back button
        oauth2.authorize()

The problem is that when I use it with Linkedin, it gives following error:

Authorization went wrong: missing required parameters, includes an
invalid parameter value, parameter more than once. : client_secret

When I use implicit-grant, I get following error, even before the user credentials form comes up:

Authorization went wrong: Invalid redirect URL:
http://www.dummyredirecturl.com/oauth.aspx?error=unsupported_response_type&error_description=We+only+support+a+response_type+of+%22code%22%2C+but+you+passed+token

Also, when I use the above code for Facebook (of-course after changing credentials) , it works correctly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.