Assent gives an Elixir app one OAuth shape across two dozen providers
Multi-provider framework in Elixir
At a glance
- What is it?
- Assent is a MIT licensed Elixir authentication framework built on three base strategies, OAuth 1.0a, OAuth 2.0 and OpenID Connect, with around two dozen provider strategies on top, from Apple Sign In to Zitadel. A provider is two functions, authorize_url and callback, and the README shows both the single-provider and multi-provider wiring in full.
- Who is it for?
- Adopt Assent if you are writing an Elixir service that has to sign users in with several identity providers and want one normalised user shape and one callback flow. Skip it if you only need a single provider, since a provider-specific client may be less code.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 133 days ago.
- What is it written in?
- Mainly Elixir, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Three base strategies and a long provider list
Assent describes itself as a multi-provider authentication framework, and the structure is two layers. The base strategies are the protocol implementations: OAuth 1.0a as Assent.Strategy.OAuth, OAuth 2.0 as Assent.Strategy.OAuth2, and OpenID Connect as Assent.Strategy.OIDC. On top of them sit the provider strategies, each of which is a thin adapter from a specific vendor onto one of the bases. The list covers Apple Sign In, Auth0, Azure AD, Basecamp, Bitbucket, DigitalOcean, Discord, Facebook, GitHub, GitLab, Google, Instagram, LINE Login, LinkedIn, Slack, Spotify, Strava, Stripe Connect, Telegram, Twitch, Twitter, VK and Zitadel. The value of that split is that adding a provider does not mean writing a protocol implementation. The repository layout is small, with lib, test and integration directories, a mix.exs and mix.lock, a .formatter.exs, a CHANGELOG.md and a LICENSE file, and the API docs are published on hexdocs.pm with the package on hex.pm.
A strategy is two functions, request and callback
The usage model is the part worth understanding before reading any code. A strategy consists of two phases, request and callback. In the request phase the user is redirected to the auth provider for authentication and then returned to initiate the callback phase. The README's single-provider example, using Assent.Strategy.Github with Plug, shows both halves. The request function builds a config of client_id, client_secret and redirect_uri, asks the strategy for an authorize_url, and on success puts the returned session_params into the session before redirecting the user with a 302. The callback function reads the query params off the connection, pulls the session_params back out of the session and deletes them, merges them into the config with Keyword.put, and calls Github.callback(params). A successful callback yields a map with a user and a token, which the example stores in the session before redirecting to the root path. Both phases return an error tuple on failure, and the example responds with a 500 in that case.
Why session_params exist between the two phases
The session_params value is the mechanism that makes the OAuth 2.0 and OpenID Connect flows work, and the example comments say so directly. They are used for OAuth 2.0 and OIDC strategies and will be retrieved when the user returns for the callback phase, so they are put into the session during the request phase and read back during the callback phase. What they carry in practice is the state value and any PKCE verifier that the flow needs to validate the response, which is precisely the data that must survive the round trip to the provider and back. The README also points out a subtlety in the callback: in this example only GET query params are expected, but the provider could also return the user with a POST request where the params are in the POST body. So the callback is not just a URL handler, it is a phase boundary where data has to be carried across a redirect safely.
One module for many providers
All Assent strategies work the same way, so the README notes that if you have more than one strategy you may want to set up a single module to handle any of them, and the example is a generalised flow similar to what is used in PowAssent. The configuration is a map keyed by provider name, each entry holding the credentials and the strategy module.
config :my_app, :strategies,
github: [
client_id: "REPLACE_WITH_CLIENT_ID",
client_secret: "REPLACE_WITH_CLIENT_SECRET",
strategy: Assent.Strategy.Github
],
# Other strategiesThe generalised module then dispatches to the configured strategy for each phase.
@spec authorize_url(atom()) :: {:ok, map()} | {:error, term()}
def authorize_url(provider) do
config = config!(provider)
config[:strategy].authorize_url()
endAnd the callback does the same with the provider's params and the stored session params.
@spec callback(atom(), map(), map()) :: {:ok, map()} | {:error, term()}
def callback(provider, params, session_params) do
config = config!(provider)
config
|> Keyword.put(:session_params, session_params)
|> config[:strategy].callback(params)
endThe private config! helper fetches the provider entry from the application environment, raises when a provider has no configuration, and puts a redirect_uri derived from the provider name into the config. That last detail is a small design decision with a consequence: the callback URL is uniform across providers, so a single route pattern serves all of them, and a misconfigured provider fails loudly at startup rather than redirecting the user somewhere wrong.
Writing your own provider on the OAuth 2.0 base
You can create custom strategies, and the README gives an OAuth 2.0 implementation built on Assent.Strategy.OAuth2.Base. The custom module uses the base module and implements default_config, where base_url is used for any paths below it, an absolute authorize_url overrides that base_url, and token_url, user_url, authorization_params and auth_method complete the description. It also implements normalize, which takes the raw provider user and returns a map conforming to the OpenID Connect Core 1.0 Standard Claims spec, with sub, name, nickname and email in the example. The trailing paragraph of the README explains the rule that trips people up. The normalized user map should conform to the OpenID Connect Core 1.0 Standard Claims spec, and should return either {:ok, userinfo_claims} or {:ok, userinfo_claims, additional}. Any keys defined in the userinfo claims that are not part of the specs will not be included in the user map, and instead should be set in the additional data, which is then merged on top of the userinfo claims.
Where Assent is the wrong choice
Three limits. The first is that Assent is a library and not an application framework. The example is raw Plug, and there is no user schema, no session storage beyond what you configure, no account linking, no email verification and no password recovery, because those live in Pow and its extensions rather than here. If you want a complete authentication stack in one dependency, look at Pow, and note that the README's multi-provider example is explicitly similar to what is used in PowAssent, which is the natural pairing. The second is the normalisation ceiling. Every provider's user ends up as a map shaped by the OpenID Connect standard claims, and anything a provider returns outside that set has to be routed through the additional data. If your application needs provider-specific fields, you are working with the merged map rather than a first-class field, and a provider that returns unusual identity information may not fit the shape at all. The third is the release cadence. The listed releases are v0.3.1 from 2025-06-20, v0.3.0 from 2025-01-06 and v0.2.13 from 2025-01-06, and the last push to the repository was on 2026-06-12, so the project is at a 0.x version with an interface that can still move. The library itself is MIT licensed, so the licence is not an obstacle, but a 0.x dependency in a critical path deserves a pin and a test.
Editorial conclusion
Adopt Assent if you are writing an Elixir service that has to sign users in with several identity providers and want one normalised user shape and one callback flow. Skip it if you only need a single provider, since a provider-specific client may be less code. Before you commit, check whether the normalised user map your provider returns includes the claims your application actually stores, and read the last paragraph of the README, which explains that claims outside the OpenID Connect standard go into the additional data rather than the user map.
Frequently asked questions
What is the Assent Elixir framework?
It is a multi-provider authentication framework with three base strategies, OAuth 1.0a as Assent.Strategy.OAuth, OAuth 2.0 as Assent.Strategy.OAuth2 and OpenID Connect as Assent.Strategy.OIDC, and around two dozen provider strategies on top, including Apple, Auth0, Azure AD, Discord, GitHub, GitLab, Google, Slack and Zitadel.
How does an Assent strategy work?
A strategy has two phases, request and callback. In the request phase the user is redirected to the auth provider, and session_params are stored in the session. In the callback phase the provider returns with params attached, which are passed along with the stored session_params to the strategy's callback function, which returns a user and a token on success.
How do I add a provider that Assent does not ship?
Write a custom strategy, for example using Assent.Strategy.OAuth2.Base, and implement default_config with the base_url, authorize_url, token_url, user_url, authorization_params and auth_method, plus a normalize function that maps the provider's user to OpenID Connect standard claims.
What happens to provider fields that are not OpenID Connect standard claims?
They will not be included in the user map. The README says they should be set in the additional data instead, and the normalize function should return either {:ok, userinfo_claims} or {:ok, userinfo_claims, additional}, with the additional data merged on top of the userinfo claims.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pow-auth-assent)Was this useful?