PhoenixApiToolkit.Security.Oauth2Plug (Phoenix API Toolkit v3.0.0) View Source

Plug to verify an Oauth2 JWT. The JWT must be passed to the application in the authorization request header prefixed with Bearer:. The Plug must be configured with a set of JSON Web Keys used to sign the JWT's, a whitelist of algorithms that may be used to sign the JWT and the expected issuer. The optional dummy_verify setting can be used to skip JWT verification for development and testing. Requires :jose dependency in your mix.exs file.

The most important setting is keyset, in which a base64-encoded JSON list of JWK's must be provided. In order to make the parsing work reliably, do the encoding in IEx or use echo 'keylist' | base64 in bash.

This plug verifies the token's signature, issuer and expiration timestamp. Depending on the Oauth2 provider used, additional token claims should be verified. According to the Oauth2 spec, the "aud" claim must always be verified, for example. In practice, however, not all Oauth2 implementations adhere to the specification by including this claim in their tokens, so that's why verification of "aud" and other claims is not handled by default by this plug. Please refer to your Oauth2 provider's instructions about verifying token claims and take a look at PhoenixApiToolkit.Security.Plugs for additional claim verification plugs.

If the token is invalid for any reason, PhoenixApiToolkit.Security.Oauth2TokenVerificationError is raised, resulting in a 401 Unauthorized response.

Usage example

plug Elixir.PhoenixApiToolkit.Security.Oauth2Plug,
  keyset: "W3siYWxnIjoiUlMyNTYiLCJlIjoiQ",
  exp_iss: "https://my_oauth2_auth_server",
  dummy_verify: false,
  alg_whitelist: ["RS256"]

Results examples

use Plug.Test
import PhoenixApiToolkit.TestHelpers
alias PhoenixApiToolkit.Security.Oauth2Plug

@jwt_defaults %{
  jwk: gen_jwk(),
  jws: gen_jws(),
  payload: gen_payload(iss: "http://my-oauth2-provider")
}

def opts do
  Oauth2Plug.init(
    lazy_keyset: fn -> test_jwks() |> Oauth2Plug.process_jwks() end,
    lazy_exp_iss: fn -> @jwt_defaults.payload["iss"] end,
    dummy_verify: false,
    alg_whitelist: ["RS256"]
  )
end

a correctly signed request is passed through, with the JWT's payload and JWS assigned
iex> conn = conn(:get, "/")
iex> jwt = gen_jwt(@jwt_defaults)
iex> result = conn |> put_jwt(jwt) |> Oauth2Plug.call(opts())
iex> result == conn |> put_jwt(jwt) |> assign(:jwt, result.assigns.jwt) |> assign(:jws, result.assigns.jws) |> assign(:raw_jwt, result.assigns.raw_jwt)
true

# requests that are noncompliant result in an Oauth2TokenVerificationError
iex> conn(:get, "/") |> Oauth2Plug.call(opts())
** (PhoenixApiToolkit.Security.Oauth2TokenVerificationError) Oauth2 token invalid: missing authorization header

iex> conn(:get, "/") |> put_jwt("invalid") |> Oauth2Plug.call(opts())
** (PhoenixApiToolkit.Security.Oauth2TokenVerificationError) Oauth2 token invalid: could not decode JWT

Link to this section Summary

Functions

Process a jwks string to pass to &call/2.

Link to this section Functions

Process a jwks string to pass to &call/2.