{"id":21980581,"url":"https://github.com/stormpath/turnstile","last_synced_at":"2025-10-23T14:47:33.273Z","repository":{"id":46711752,"uuid":"64418324","full_name":"stormpath/Turnstile","owner":"stormpath","description":"An authentication framework for Swift. ","archived":false,"fork":false,"pushed_at":"2017-03-06T14:38:35.000Z","size":168,"stargazers_count":164,"open_issues_count":6,"forks_count":29,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-04-30T05:04:05.882Z","etag":null,"topics":["authentication","facebook","google","login","oauth","perfect","server","swift","vapor"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stormpath.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-28T18:20:38.000Z","updated_at":"2024-12-12T10:44:26.000Z","dependencies_parsed_at":"2022-08-02T14:15:49.563Z","dependency_job_id":null,"html_url":"https://github.com/stormpath/Turnstile","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/stormpath/Turnstile","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormpath%2FTurnstile","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormpath%2FTurnstile/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormpath%2FTurnstile/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormpath%2FTurnstile/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stormpath","download_url":"https://codeload.github.com/stormpath/Turnstile/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stormpath%2FTurnstile/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261795335,"owners_count":23210620,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","facebook","google","login","oauth","perfect","server","swift","vapor"],"created_at":"2024-11-29T17:12:06.727Z","updated_at":"2025-10-23T14:47:28.189Z","avatar_url":"https://github.com/stormpath.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"#Stormpath is Joining Okta\nWe are incredibly excited to announce that [Stormpath is joining forces with Okta](https://stormpath.com/blog/stormpaths-new-path?utm_source=github\u0026utm_medium=readme\u0026utm-campaign=okta-announcement). Please visit [the Migration FAQs](https://stormpath.com/oktaplusstormpath?utm_source=github\u0026utm_medium=readme\u0026utm-campaign=okta-announcement) for a detailed look at what this means for Stormpath users.\n\nWe're available to answer all questions at [support@stormpath.com](mailto:support@stormpath.com).\n\n# Turnstile\n[![Build Status](https://api.travis-ci.org/stormpath/Turnstile.svg?branch=master)](https://travis-ci.org/stormpath/Turnstile)\n[![codecov](https://codecov.io/gh/stormpath/Turnstile/branch/master/graph/badge.svg)](https://codecov.io/gh/stormpath/Turnstile)\n[![codebeat badge](https://codebeat.co/badges/ba981396-2a2c-4364-8067-6c128758f3bc)](https://codebeat.co/projects/github-com-stormpath-turnstile)\n[![Slack Status](https://talkstormpath.shipit.xyz/badge.svg)](https://talkstormpath.shipit.xyz)\n\nTurnstile is a security framework for Swift inspired by [Apache Shiro](http://shiro.apache.org). It's used to manage the currently executing user account in your application, whether iOS app or backend web application. \n\n# Overview\n\nTurnstile is the easiest way to add authentication to your Swift apps. Currently, the focus is on a great API for backend Swift apps. \n\nTurnstile is split into three projects:\n\n* Turnstile Core - provides key components for authentication and security. \n* Turnstile Crypto - tools for generating randomness, hashing, and encrypting data\n* Turnstile Web - integrations with Facebook, Google, and other helpers useful for backend web applications. \n\nIf you're a developer of an application or product and need to integrate Turnstile, read the docs for Turnstile Core. Otherwise, if you're a developer using a Turnstile integration, read the docs for Turnstile Web.\n\n# Getting Started\n\nThe easiest way to use Turnstile is with one of its prebuilt integrations with Swift web frameworks. Here are the frameworks and their statuses:\n\n* [Vapor](https://github.com/vapor/vapor) - Turnstile is integrated directly into Vapor.\n* [Perfect](https://github.com/PerfectlySoft/Perfect) - The [Turnstile-Perfect](https://github.com/stormpath/Turnstile-Perfect) integration allows you to use Turnstile with Perfect. \n* [Zewo](http://www.zewo.io) - A [Zewo integration](https://github.com/Zewo/TurnstileMiddleware) is in development.\n* [Kitura](https://github.com/IBM-Swift/Kitura) - A Kitura integration is planned.\n\n# Using Turnstile\n\nIf you'd like to use Turnstile to build your own integration, it's useful to understand key concepts in Turnstile. \n\n## Subject\n\nThe `Subject` represents the currently operating user for your application. You'll use this to interact with Turnstile, and safely check if the current user is authenticated properly. \n\nThe Subject API in Turnstile also supports registration, however this is a convenience for basic use cases. Since different apps have different rules on registration and user managment, it's expected that you will most likely write your own registration and user management logic.\n\n## Realm\n\nA realm connects Turnstile to your data store, and allows Turnstile to authenticate and register accounts. Included with Turnstile is a `MemoryRealm`, as well as a `WebMemoryRealm` which can handle username/password pairs, as well as Facebook and Google login. \n\nThe MemoryRealms store information in memory, and will be wiped when the application is restarted. \n\nTo write your own Realm, you'll need to implement the `Realm` protocol, which is defined as: \n\n```Swift\npublic protocol Realm {\n  func authenticate(credentials: Credentials) throws -\u003e Account\n  func register(credentials: Credentials) throws -\u003e Account\n}\n```\n\nTurnstile provides `Credentials` objects for common use cases, like `UsernamePassword`, `APIKey`, and `AccessToken`. Feel free to define a custom type as well. \n\nWhen Turnstile calls the `authenticate` or `register` functions, your Realm should check the Credential type and make sure that it's a credential type you support. If not, it should `throw UnsupportedCredentialsError()`.\n\nAfterwards, your Realm should check if the credentials are valid. If not, it should `throw IncorrectCredentialsError()`.\n\nIf the credentials are correct, you should then authenticate the user, and return the account object. The account protocol is simple:\n\n```Swift\npublic protocol Account {\n    var uniqueID: String { get }\n}\n```\n\nAnd voila! You've created your first Realm!\n\n## SessionManager\n\nSessionManager is a Turnstile component that manages sessions and persistience for your authentication system. Included with Turnstile is a `MemorySessionmanager`, which can persist sessions in memory. \n\nIf you're building your own, you'll need to implement the `SessionManager` protocol. This is defined as:\n\n```Swift\npublic protocol SessionManager {\n    /// Creates a session for a given Account object and returns the identifier.\n    func createSession(account: Account) -\u003e String\n    \n    /// Gets the account ID for the current session identifier.\n    func restoreAccount(fromSessionID identifier: String) throws -\u003e Account\n\n    /// Destroys the session for a session identifier.\n    func destroySession(identifier: String)\n}\n```\n\nWhen an account is authenticated, and is asks to use the session manager to persist its data, Turnstile calls `createSession(account:)` and expects the Session Manager to return a SessionID it can use to restore the account. While you can use whatever you want as a Session ID, we recommend using TurnstileCrypto's `Random.secureToken` method to generate a random string with 128 bits of entropy. \n\nWhen a user comes in with the SessionID, Turnstile calls `restoreAccount(fromSessionID:)` and expects the session manager to return the associated account. Note that this can be different from the account in the Realm, since you might not want to make a database call on every request. If the session does not exist, the Session Manager should `throw InvalidSessionError()`\n\nWhen the user logs out, `destroySession(identifier:)` should delete the session from the session store. \n\n# Turnstile Web\n\nTurnstile Web provides a number of helpers to make authentication for websites easier. TurnstileWeb includes plugins for external login providers, like Facebook, Google, and Digits. \n\n## OAuth2: Authenticating with Facebook or Google\n\nThe Facebook and Google Login flows look like the following:\n\n1. Your web application redirects the user to the Facebook / Google login page, and saves a \"state\" to prevent a malicious attacker from hijacking the login session. \n2. The user logs in.\n3. Facebook / Google redirects the user back to your application. \n4. The application validates the Facebook / Google token as well as the state, and logs the user in. \n\n### Create a Facebook Application\n\nTo get started, you first need to [register an application](https://developers.facebook.com/?advanced_app_create=true) with Facebook. After registering your app, go into your app dashboard's settings page. Add the Facebook Login product, and save the changes. \n\nIn the `Valid OAuth redirect URIs` box, type in a URL you'll use for step 3 in the OAuth process. (eg, `http://localhost:8080/login/facebook/consumer`)\n\n### Create a Google Application\n\nTo get started, you first need to [register an application](https://console.developers.google.com/project) with Google. Click \"Enable and Manage APIs\", and then the [credentials tab](https://console.developers.google.com/apis/credentials). Create an OAuth Client ID for \"Web\".\n\nAdd a URL you'll use for step 3 in the OAuth process to the `Authorized redirect URIs` list. (eg, `http://localhost:8080/login/google/consumer`)\n\n### Initiating the Login Redirect\n\nTurnstileWeb has `Facebook` and `Google` objects, which will allow a you to set up your configured application and log users in. To initialize them, use the client ID and secret (sometimes called App ID) from your Facebook or Google developer console:\n\n```Swift\nlet facebook = Facebook(clientID: \"clientID\", clientSecret: \"clientSecret\")\nlet google = Google(clientID: \"clientID\", clientSecret: \"clientSecret\")\n```\n\nThen, generate a state (you can use Random.secureToken to generate a random string), save it (we recommend setting a cookie on your user's browser), and redirect the user:\n\n```Swift\n// Redirect the user to this URL using your web framework:\nfacebook.getLoginLink(redirectURL: \"http://localhost:8080/login/google/consumer\", state: state)\n```\n\n### Consuming the Login Response\n\nOnce the user is redirected back to your application, you can now verify that they've properly authenticated using the `state` from the earlier step, and the full URL that the user has been redirected to:\n\n```Swift\nlet credentials = try facebook.authenticate(authorizationCodeCallbackURL: url, state: state) as! FacebookAccount\nlet credentials = try google.authenticate(authorizationCodeCallbackURL: url, state: state) as! GoogleAccount\n```\n\nThese can throw the following errors:\n\n* `InvalidAuthorizationCodeError` if the Authorization Code could not be validated\n* `APIConnectionError` if we cannot connect to the OAuth server\n* `InvalidAPIResponse` if the server does not respond in a way we expect\n* `OAuth2Error` if the OAuth server calls back with an error\n\n\nIf successful, it will return a `FacebookAccount` or `GoogleAccount`. These implement the `Credentials` protocol, so then can be passed back into your application's Realm for further validation.\n\n## OAuthEcho: Authenticating with Twitter or Digits\n\nThe Digits Login flows look like the following:\n\n1. Your application prompts for login either with Twitter or Digits\n2. The user selects one and logs in.\n3. Digits generates two special headers:\n  - `X-Auth-Service-Provider` the endpoint where the user needs to be authenticated through\n  - `X-Verify-Credentials-Authorization` the OAuth token\n4. The application validates the information in the generated headers\n5. The application makes a `GET` request to the URL in the `X-Auth-Service-Provider` header with the `X-Verify-Credentials-Authorization` header added as the `Authorization` header in this request.\n6. The response is validated to authorize the user and logs them in\n\n### Create a Digits Application\n\nThe easiest way to setup your app with Digits is to use the [Fabric app](https://fabric.io/).  After you go through the setup you will then be able to access the `consumerKey` and `consumerSecret` in the Fabric web interface.  \n\n### Example implementation\n\n```Swift\nlet digits = Digits(consumerKey: \"consumerKeyGoesHere\")\nguard\n    let urlString = request.headers[\"X-Auth-Service-Provider\"],\n    let url = URL(string: urlString),\n    let authHeader = request.headers[\"X-Verify-Credentials-Authorization\"],\n    let oauthParams = OAuthParameters(header: authHeader)\nelse {\n    throw Abort.custom(status: .unauthorized, message: \"Bad Digits headers\")\n}\n\nlet credentials: Credentials? = OAuthEcho(authServiceProvider: url, oauthParameters: oauthParams)\nlet account = try digits.authenticate(credentials: credentials!) as! DigitsAccount\n```\n\nFor an example of Digits in action using Vapor checkout [this app](https://github.com/kdawgwilk/KarmaAPI)\n\n# TurnstileCrypto\n\nTurnstile Crypto has tools to help you build authentication in your apps. Specifically, it can help you use BCrypt hashing in your app, as well as generate secure random numbers. Documentation is in the files themselves. \n\n# Tests\n\nTests are powered by XCTest. To successfully perform the Facebook Login tests, you must have the following environment variables set:\n\n* `FACEBOOK_CLIENT_ID` - the Facebook App ID for a test app from the [Facebook developer console](https://developers.facebook.com). \n* `FACEBOOK_CLIENT_SECRET` - the Facebook App Secret for a test app from the [Facebook developer console](https://developers.facebook.com). \n\n# Contributing\n\nWe're always open to contributions! Feel free to join the [Stormpath slack channel](https://talkstormpath.shipit.xyz) to discuss how you can contribute!\n\n# Stormpath\n\nTurnstile is built by [Stormpath](https://stormpath.com), an API service for authentication, authorization, and user management. If you're building a website, API, or app, and need to build authentication and user management, consider using Stormpath for your needs. We're always happy to help!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstormpath%2Fturnstile","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstormpath%2Fturnstile","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstormpath%2Fturnstile/lists"}