A simple OAuth library for iOS with a built-in set of providers

Overview

Travis Status CocoaPods compatible Carthage compatible

SwiftyOAuth is a small OAuth library with a built-in set of providers and a nice API to add your owns.

let instagram: Provider = .instagram(clientID: "***", redirectURL: "foo://callback")

instagram.authorize { result in
    print(result) // success(Token(accessToken: "abc123"))
}

UsageProvidersInstallationLicense

Usage

Provider

Provider.swift

Step 1: Create a provider

Initialize a provider with the custom URL scheme that you defined:

// Provider using the server-side (explicit) flow

let provider = Provider(
    clientID:     "***",
    clientSecret: "***",
    authorizeURL: "https://example.com/authorize",
    tokenURL:     "https://example.com/authorize/token",
    redirectURL:  "foo://callback"
)

// Provider using the client-side (implicit) flow

let provider = Provider(
    clientID:     "***",
    authorizeURL: "https://example.com/authorize",
    redirectURL:  "foo://callback"
)

// Provider using the client-credentials flow

let provider = Provider(
    clientID:     "***",
    clientSecret: "***"
)

Alternatively, you can use one of the built-in providers:

let github = .gitHub(
    clientID:     "***",
    clientSecret: "***",
    redirectURL:  "foo://callback"
)

Optionally set the state and scopes properties:

github.state  = "asdfjkl;" // An random string used to protect against CSRF attacks.
github.scopes = ["user", "repo"]

Use a WKWebView if the provider doesn't support custom URL schemes as redirect URLs.

let provider = Provider(
    clientID:     "***",
    clientSecret: "***",
    authorizeURL: "https://example.com/authorize",
    tokenURL:     "https://example.com/authorize/token",
    redirectURL:  "https://an-arbitrary-redirect-url/redirect"
)

provider.useWebView = true

Define additional parameters for the authorization request or the token request with additionalAuthRequestParams and additionalTokenRequestParams respectively:

github.additionalAuthRequestParams["allow_signup"] = "false"
Step 2: Handle the incoming requests

Handle the incoming requests in your AppDelegate:

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    github.handleURL(url, options: options)

    return true
}
Step 3: Ask for authorization

Finally, ask for authorization. SwiftyOAuth will either present a SFSafariViewController (iOS 9) or open mobile safari.

github.authorize { (result: Result<Token, Error>) -> Void in
    switch result {
    case .success(let token): print(token)
    case .failure(let error): print(error)
    }
}

If the provider provides an expirable token, you may want to refresh it.

let uber: Provider = .uber(
    clientID: "***",
    clientSecret: "***",
    redirectURL: "foo://callback/uber"
)

// uber.token!.isExpired => true

uber.refreshToken { result in
    switch result {
    case .success(let token): print(token)
    case .failure(let error): print(error)
    }
}

Token

Token.swift

The access_token, token_type, scopes, and informations related to the expiration are available as Token properties:

token.accessToken // abc123
token.tokenType   // .Bearer
token.scopes      // ["user", "repo"]

token.expiresIn // 123
token.isExpired // false
token.isValid   // true

Additionally, you can access all the token data via the dictionary property:

token.dictionary // ["access_token": "abc123", "token_type": "bearer", "scope": "user repo"]

Token Store

Every Token is stored and retrieved through an object that conforms to the TokenStore protocol.

The library currently supports following TokenStores:

provider.tokenStore = Keychain.shared

Keychain: Before you use thisTokenStore, make sure you turn on the Keychain Sharing capability.

provider.tokenStore = UserDefault.standard

UserDefaults: the default TokenStore. Information are saved locally and, if properly initialized, to your App Group.

provider.tokenStore = NSUbiquitousKeyValueStore.default

NSUbiquitousKeyValueStore: the information are saved in the iCloud Key Value Store. Before you use this TokenStore make sure your project has been properly configured as described here.

Error

Error.swift

Error is a enum that conforms to the ErrorType protocol.

  • cancel The user cancelled the authorization process by closing the web browser window.

  • applicationSuspended The OAuth application you set up has been suspended.

  • redirectURIMismatch The provided redirectURL that doesn't match what you've registered with your application.

  • accessDenied The user rejects access to your application.

  • invalidClient The clientID and or clientSecret you passed are incorrect.

  • invalidGrant The verification code you passed is incorrect, expired, or doesn't match what you received in the first request for authorization.

  • other The application emitted a response in the form of {"error": "xxx", "error_description": "yyy"} but SwiftyOAuth doesn't have a enum for it. The data is available in the associated values.

  • unknown The application emitted a response that is neither in the form of a success one ({"access_token": "xxx"...}) nor in the form of a failure one ({"error": "xxx"...}). The data is available in the associated value.

  • nsError An error triggered when making network requests or parsing JSON. The data is available in the associated value.

Providers

Providers/

Check the wiki for more informations!

Installation

Carthage

Carthage is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate SwiftyOAuth into your Xcode project using Carthage, specify it in your Cartfile:

github "delba/SwiftyOAuth" >= 1.1

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

You can install it with the following command:

$ gem install cocoapods

To integrate SwiftyOAuth into your Xcode project using CocoaPods, specify it in your Podfile:

use_frameworks!

pod 'SwiftyOAuth', '~> 1.1'

License

Copyright (c) 2016-2019 Damien (http://delba.io)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Comments
  • Authentication with Google: not matching redirect URL

    Authentication with Google: not matching redirect URL

    I was trying to perform a login with Google Service.

    Hereby the executed code

    
    let provider = Provider.Google(clientID: "$(the_client_id)", clientSecret: "$(the_client_secret)", redirectURL: "$(bundle_identifier):/urn:ietf:wg:oauth:2.0:oob")
    
    provider.scopes = ["https://www.googleapis.com/auth/analytics.readonly"]
    
    provider.authorize(self) { (result) in
        switch result {
        case .Success(let token):
            print(token)
        case .Failure(let error):
            print(error)
    }
    

    When the authorisation is granted in the Safari View Controller the redirect URL gets triggered with following format:

    $(bundle_identifier):/urn:ietf:wg:oauth:2.0:oob?code=$(the_authorization_code)
    

    This cause an issue for when the library checks if the redirect URL use during the initialisation of the Provider matches the received redirect URL from the AppDelegate.

    I've tried other combination of redirect URL but that's the redirect URL format that the Google guide advocates.

    Did anyone experienced this when trying the built in Google Provider?

    I think an easy fix would be to match the retrieved URL by removing the GET parameter from the URL but before open a new PR I'd like to receive some feedback about this.

    opened by fabiomassimo 34
  • Multi platform support

    Multi platform support

    The OAuth Authentication process nowadays can be triggered from many devices equipped with different os: watch OS, iOS, tvOS.

    Unfortunately, not all of this operating system can rely on a safe way to implement the OAuth 2.0 authentication flow:

    • tvOS does not support SafariServices or Webkit.
    • watchOS does not support SafariServices or Webkit.
    • Today extension can not present webpages.

    My idea to address this issue would be to implement for SwiftyOAuth following goals:

    • [ ] Make the library use app extension API only.
    • [ ] Add support for multiple platform: iOS, watchOS, tvOS.
    • [ ] Improve -authorize method depending on current platform and capabilities.

    What do you think?

    opened by fabiomassimo 10
  • Issue in following the README for handling incoming requests in the AppDelegate

    Issue in following the README for handling incoming requests in the AppDelegate

    From the docs:

    Step 2: Handle the incoming requests
    
    Handle the incoming requests in your AppDelegate:
    
    func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
        github.handleURL(url, options: options)
    
        return true
    }
    

    Unfortunately, while I was following the README, I found out that that the delegate method signature is slightly different:

    func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool
    

    This causes an inconsistency with the designated method to handle an URL from the Provider.

        /**
         Handles the incoming URL.
    
         - parameter URL:     The incoming URL to handle.
         - parameter options: A dictionary of launch options.
         */
        public func handleURL(URL: NSURL, options: [String : AnyObject])
    

    I think it should be rewritten in something like:

    public func handleURL(URL: NSURL, sourceApplication: String?) 
    

    Such that the Step 2 from the README would look like:

    func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
        github.handleURL(url, sourceApplication: sourceApplication)
    
        return true
    }
    
    opened by fabiomassimo 7
  • Instagram does not supply token_type and the flow breaks

    Instagram does not supply token_type and the flow breaks

    In Token.swift, this init makes the all 3 parameters mandatory:

            guard let
                accessToken = json["access_token"] as? String,
                tokenType = json["token_type"] as? String,
                scope = json["scope"] as? String
            else { return nil }
    

    However, Instagram only returns access_token

    {
        "access_token": "2342264381.bc3c943.6086017a221b427a997e4928f0ea197b",
        "user": {
    ...
        }
    }
    

    What do you think about this:

        internal init?(json: JSON) {
            guard let
                accessToken = json["access_token"] as? String
            else { return nil }
    
            self.accessToken = accessToken
    
            self.tokenType = json["token_type"] as? String ?? ""
            self.scope = json["scope"] as? String ?? ""
    
            self.dictionary = json
        }
    
    opened by radianttap 7
  • Authenticate NSURLRequest

    Authenticate NSURLRequest

    Summary

    Authenticate NSURLRequest as specified by OAuth 2.0 guidelines for token type Bearer.

    The Provider exposes a convenience method that, in case the available token is expired, refreshes the token and then uses it to authenticate any NSURLRequest.

    Why

    This feature give to the client an easy way to integrate a valid access token, retrieved via OAuth 2.0, in any NSURLRequest that the client might have created beforehand.

    Note

    To improve test cases accuracy I needed to add a dependency for the test target: OHTTPStubs.

    I found the best way to do it via Carthage. To get started run the shell script in the bin folder.

    opened by fabiomassimo 5
  • Created `TokenStore` protocol for storing `Token` type

    Created `TokenStore` protocol for storing `Token` type

    Summary

    Abstracted the logic for storing and retrieving a Token to a protocol, such that any object that conforms to it can be used to store and retrieve a Token type.

    Why

    By abstracting the implementation of the logic responsible to store/retrieve an object to a protocol, the user's of the library can provide his own [..]TokenStore that better fits his needs.

    i.e.: this feature made possible to support iCloud Key Value Store very easily by gaining the opportunity to store a Token in the iCloud Key Value Store Container.

    Note

    As proof of concept I've implemented this protocol to support NSUserDefaults and NSUbiquitousKeyValueStore.

    opened by fabiomassimo 5
  • Swift 3.0

    Swift 3.0

    Summary

    Implementation for #13

    • [x] Add new grant type: http://oauth.net/grant_type/device/1.0
    • [x] Handle new error cases: "authorisation_pending" , "slow_down", "code_expired"
    • [x] Extend the Provider with support to request an access token with new grant type.
    opened by fabiomassimo 4
  • Topic token dictionary

    Topic token dictionary

    When storing the existing Token, it already has proper created_at value. When restoring back using init(dictionary:) that original value should not be over-written.

    opened by radianttap 3
  • Keychain token store

    Keychain token store

    As requested, keychain support should not be based on third-party libraries, but uses a small wrapper found at https://gist.github.com/jackreichert/414623731241c95f0e20

    Hint: func setToken() should return a Bool value, but has to be changed at protocol level (TokenStore).

    opened by johannwilfling 3
  • Improved feature for `Error` enum struct

    Improved feature for `Error` enum struct

    Summary

    Improved initialisation and features provided by the Error enum struct in order to provide a better error handling in the library

    Why

    Currently there are few places in the code marked with a TODO about a better error handling. I hope this will help.

    opened by fabiomassimo 3
  • How to resolve view is not in window hierarchy error?

    How to resolve view is not in window hierarchy error?

    Attempt to present <SFSafariViewController: 0x7fc7ba018400> on <MyOauth.ViewController: 0x7fc7b860b070> whose view is not in the window hierarchy!

    opened by udayathreya 1
  • Topic self signed

    Topic self signed

    I hate to re-invent hot water so I picked up the Server Trust handling from Alamofire.

    Added Extension to URLSession to pick up current serverTrustPolicy from HTTPConfig struct, which is public and can be set from any other module.

    opened by radianttap 2
  • Support OAuth 2.0 Device Flow

    Support OAuth 2.0 Device Flow

    Device flow's goal is to implement OAuth 2.0 authorisation flow on devices with limited capabilities (i.e. no WebKit).

    In my overview to support this:

    • [ ] Add new grant type: http://oauth.net/grant_type/device/1.0
    • [ ] Handle new error cases: "authorisation_pending" , "slow_down", "code_expired"
    • [ ] Extend the Provider with support to request an access token with new grant type.
    opened by fabiomassimo 12
Releases(v0.3)
  • v0.3(May 26, 2016)

    Refresh the tokens:

    provider.refreshToken { result in
        // ...
    }
    

    Check if a token is valid:

    if let token = provider.token {
        print(token.isValid)
        print(token.isExpired)
    }
    

    Optionally set the scopes:

    provider.scopes = ["some", "scope", "here"]
    

    Optionally set the additional parameters for the authorization/token requests:

    provider.additionalAuthRequestParams
    provider.additionalTokenRequestParams
    

    A new wiki 😃

    Source code(tar.gz)
    Source code(zip)
Owner
Damien
Damien
Swift based OAuth library for iOS

OAuthSwift Swift based OAuth library for iOS and macOS. Support OAuth1.0, OAuth2.0 Twitter, Flickr, Github, Instagram, Foursquare, Fitbit, Withings, L

OAuthSwift 3.1k Jan 6, 2023
Snap Scraper enables users to download media uploaded to Snapchat's Snap Map using a set of latitude and longitude coordinates.

Snap Scraper Description Snap Scraper is an open source intelligence tool which enables users to download media uploaded to Snapchat's Snap Map using

Dr Richard Matthews 58 Dec 12, 2022
Nice category that adds the ability to set the retry interval, retry count and progressiveness.

If a request timed out, you usually have to call that request again by yourself. AFNetworking+RetryPolicy is an objective-c category that adds the abi

Jakub Truhlář 209 Dec 2, 2022
Easy and lightweight network layer for creating different set of network requests like GET, POST, PUT, DELETE customizable with coders conforming to TopLevelDecoder, TopLevelEncoder

Easy and lightweight network layer for creating different set of network requests like GET, POST, PUT, DELETE customizable with coders conforming to TopLevelDecoder, TopLevelEncoder

Igor 2 Sep 16, 2022
Another network wrapper for URLSession. Built to be simple, small and easy to create tests at the network layer of your application.

Another network wrapper for URLSession. Built to be simple, small and easy to create tests at the network layer of your application. Install Carthage

Ronan Rodrigo Nunes 89 Dec 26, 2022
🤵🏽‍♀️ Janet — A thin HTTP networking layer built on URLSession for simple, declarative endpoint specification leveraging the power of async/await.

????‍♀️ Janet — Just another networking kit — A thin HTTP networking layer built on URLSession for simple, declarative endpoint specification leveragi

Niklas Holloh 3 Sep 6, 2022
Malibu is a networking library built on promises

Description Palm trees, coral reefs and breaking waves. Welcome to the surf club Malibu, a networking library built on promises. It's more than just a

Vadym Markov 410 Dec 30, 2022
Malibu is a networking library built on promises

Description Palm trees, coral reefs and breaking waves. Welcome to the surf club Malibu, a networking library built on promises. It's more than just a

HyperRedink 10 Jan 29, 2022
A native, lightweight and secure time-based (TOTP) & counter-based (HOTP) password client built for iOS

A native, lightweight and secure time-based (TOTP) & counter-based (HOTP) password client built for iOS Built by Tijme Gommers – Buy me a coffee via P

Raivo OTP 770 Jan 8, 2023
Open source Reddit client for iOS built entirely in Swift

Area51 Area51 is an open source Reddit client for iOS built entirely in Swift! Get the public beta on TestFlight Join the public Slack channel to coll

Kris 141 Dec 26, 2022
A web API client in Swift built using Async/Await

Get A modern web API client in Swift built using Async/Await and Actors. let cli

Alexander Grebenyuk 745 Jan 3, 2023
FlyingFox - a lightweight HTTP server built using Swift Concurrency

Usage Credits Introduction FlyingFox is a lightweight HTTP server built using Swift Concurrency. The server uses non blocking BSD sockets, handling ea

Simon Whitty 262 Dec 24, 2022
QwikHttp is a robust, yet lightweight and simple to use HTTP networking library for iOS, tvOS and watchOS

QwikHttp is a robust, yet lightweight and simple to use HTTP networking library. It allows you to customize every aspect of your http requests within a single line of code, using a Builder style syntax to keep your code super clean.

Logan Sease 2 Mar 20, 2022
A simple, lighweight library for making http requets in iOS

HttpKIT Super simple, super lighweight library for making http requets in IOS. It is a work in progress so PR's are definitelty welcome and highly enc

null 0 Dec 5, 2021
SwiftCANLib is a library used to process Controller Area Network (CAN) frames utilizing the Linux kernel open source library SOCKETCAN.

SwiftCANLib SwiftCANLib is a library used to process Controller Area Network (CAN) frames utilizing the Linux kernel open source library SOCKETCAN. Th

Tim Wise 4 Oct 25, 2021
Simple iOS app in Swift to show AQI for some cities using websocket using Combine + MVVM

AQI Simple iOS app in Swift to show AQI for some cities using websocket using Combine + MVVM This app follows MVVM This app uses combine framework The

Amey Vikkram Tiwari 2 Nov 6, 2022
A simple Last.fm client for iOS

vinylogue for Last.fm Vinylogue is a simple Last.fm client for iOS that shows you and your friends' charts from previous years. App Store (it's free).

Chris Trott 112 Dec 18, 2022
Login-screen-UI - A simple iOS login screen written in Swift 5

This project has been updated to Swift 5 and Xcode 11.2 About This is a simple i

Kushal Shingote 2 Feb 4, 2022
A lightweight, one line setup, iOS / OSX network debugging library! 🦊

Netfox provides a quick look on all executed network requests performed by your iOS or OSX app. It grabs all requests - of course yours, requests from

Christos Kasketis 3.4k Dec 28, 2022