brokenhandsio/vapor-oauth
OAuth2 Provider Library for Vapor
Getting Started
Vapor OAuth can be added to your Vapor add with a simple provider. To get started, first add the library to your Package.swift dependencies:
dependencies: [
...,
.package(url: "https://github.com/brokenhandsio/vapor-oauth", from: "0.6.0"))
]Next import the library into where you set up your Droplet:
import VaporOAuthThen add the provider to your Config:
try addProvider(VaporOAuth.Provider(codeManager: MyCodeManager(), tokenManager: MyTokenManager(), clientRetriever: MyClientRetriever(), authorizeHandler: MyAuthHandler(), userManager: MyUserManager(), validScopes: ["view_profile", "edit_profile"], resourceServerRetriever: MyResourceServerRetriever()))To integrate the library, you need to set up a number of things, which implement the various protocols required:
CodeManager- this is responsible for generating and managing OAuth Codes. It is only required for the Authorization Code flow, so if you do not want to support this grant, you can leave out this parameter and use the default implementationTokenManager- this is responsible for generating and managing Access and Refresh Tokens. You can either store these in memory, in Fluent, or with any backend.ClientRetriever- this is responsible for getting all of the clients you want to support in your app. If you want to be able to dynamically add clients then you will need to make sure you can do that with your implementation. If you only want to support a set group of clients, you can use theStaticClientRetrieverwhich is provided for youAuthorizeHandler- this is responsible for allowing users to allow/deny authorization requests. See below for more details. If you do not want to support this grant type you can exclude this parameter and use the default implementationUserManager- this is responsible for authenticating and getting users for the Password Credentials flow. If you do not want to support this flow, you can exclude this parameter and use the default implementation.validScopes- this is an optional array of scopes that you wish to support in your system.ResourceServerRetriever- this is only required if using the Token Introspection Endpoint and is what is used to authenticate resource servers trying to access the endpoint
Note that there are a number of default implementations for the different required protocols for Fluent in the Vapor OAuth Fluent package.
The Provider will then register endpoints for authorization and tokens at /oauth/authorize and /oauth/token
Protecting Endpoints
Vapor OAuth has a helper extension on Request to allow you to easily protect your API routes. For instance, let's say that you want to ensure that one route is accessed only with tokens with the profile scope, you can do:
try request.oauth.assertScopes(["profile"])This will throw a 401 error if the token is not valid or does not contain the profile scope. This is so common, that there is a dedicated OAuth2ScopeMiddleware for this behaviour. You just need to initialise this with an array of scopes that must be required for that protect group. If you initialise it with a nil array, then it will just make sure that the token is valid.
You can also get the user with try request.oauth.user().
Protecting Resource Servers With Remote Auth Server
If you have resource servers that are not the same server as the OAuth server that you wish to protect using the Token Introspection Endpoint, things are slightly different. See the Token Introspection section for more information.
Grant Types
Implicit Grant
The Implicit Grant is almost identical to the Authorize Code flow, except instead of being redirected back with a code which you then exchange for a token, you get redirected back with the token in the fragment. It is up to the client (such as an iOS application) to then parse the token out of the redirect URI fragment.
This flow was designed for clients where you couldn't guarantee the security of the client secret, client-side apps, but has fallen out of favour recently and it is generally recommended to use the Authorization Code flow without a client secret instead.
Resource Owner Password Credentials Grant
The Password Credentials flow should only be used for first party applications, and Vapor OAuth mandates this. This flow allows the client to collect the username and password of the user and submit them directly to the OAuth server to get a token.
Note that if you are using the password flow, as per the specification, you must secure your endpoint against brute force attacks with rate limiting or generating alerts. The library will output a warning message to the console for any unauthorized attempts, which you can use for this purpose. The message is in the form of LOGIN WARNING: Invalid login attempt for user <USERNAME>.
Client Credentials Grant
Client Credentials is a userless flow and is designed for servers accessing other servers without the need for a user. Access is granted based upon the authentication of the client requesting access.
Token Introspection
If running a microservices architecture it is useful to have a single server that handles authorization, which all the other resource servers query. To do this, you can use the Token Introspection Endpoint extension. In Vapor OAuth, this adds an endpoint you can post tokens tokens at /oauth/token_info.
You can send a POST request to this endpoint with a single parameter, token, which contains the OAuth token you want to check. If it is valid and active, then it will return a JSON payload, that looks similar to:
{
"active": true,
"client_id": "ABDED0123456",
"scope": "email profile",
"exp": 1503445858,
"user_id": "12345678",
"username": "hansolo",
"email_address": "hansolo@therebelalliance.com"
}If the token has expired or does not exist then it will simply return:
{
"active": false
}This endpoint is protected using HTTP Basic Authentication so you need to send an Authorization: Basic abc header with the request. This will check the ResourceServerRetriever for the username and password sent.
Note: as per the spec - the token introspection endpoint MUST be protected by HTTPS - this means the server must be behind a TLS certificate (commonly known as SSL). Vapor OAuth leaves this up to the integrating library to implement.
Protecting Endpoints
To protect resources on other servers with OAuth using the Token Introspection endpoint, you either need to use the OAuth2TokenIntrospectionMiddleware on your routes that you want to protect, or you need to manually set up the Helper object (the middleware does this for you). Both the middleware and helper setup require:
tokenIntrospectionEndpoint- the endpoint where the token can be validatedclient- theDroplet's client to send the token validation request withresourceServerUsername- the username of the resource serverresourceServerPassword- the password of the resource server
Once either of these has been set up, you can then call request.oauth.user() or request.oauth.assertScopes() like normal.
Package Metadata
Repository: brokenhandsio/vapor-oauth
Stars: 154
Forks: 17
Open issues: 2
Default branch: main
Primary language: swift
License: MIT
Topics: brokenhands, oauth, oauth2, oauth2-server, server-side-swift, swift, vapor, vapor-oauth, vapor-provider
README: README.md