# `Oidcc.Token`
[🔗](https://github.com/erlef/oidcc/blob/ee3434ddec86c14471af8f8a8f159971e654da3c
/lib/oidcc/token.ex#L4)

Facilitate OpenID Code/Token Exchanges

## Telemetry

* `[:oidcc, :request_token, :start]`
  * Description: Emitted at the start of requesting a code token
  * Measurements: `%{system_time: non_neg_integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :request_token, :stop]`
  * Description: Emitted at the end of requesting a code token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :request_token, :exception]`
  * Description: Emitted at the end of requesting a code token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :refresh_token, :start]`
  * Description: Emitted at the start of refreshing a token
  * Measurements: `%{system_time: non_neg_integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :refresh_token, :stop]`
  * Description: Emitted at the end of refreshing a token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :refresh_token, :exception]`
  * Description: Emitted at the end of refreshing a token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :jwt_profile_token, :start]`
  * Description: Emitted at the start of exchanging a JWT profile token
  * Measurements: `%{system_time: non_neg_integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :jwt_profile_token, :stop]`
  * Description: Emitted at the end of exchanging a JWT profile token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :jwt_profile_token, :exception]`
  * Description: Emitted at the end of exchanging a JWT profile token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :client_credentials, :start]`
  * Description: Emitted at the start of requesting a client credentials token
  * Measurements: `%{system_time: non_neg_integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :client_credentials, :stop]`
  * Description: Emitted at the end of requesting a client credentials token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

* `[:oidcc, :client_credentials, :exception]`
  * Description: Emitted at the end of requesting a client credentials token
  * Measurements: `%{duration: integer(), monotonic_time: integer()}`
  * Metadata: `%{issuer: :uri_string.uri_string(), client_id: String.t()}`

# `retrieve_opts`
*since 3.0.0* 

```elixir
@type retrieve_opts() :: :oidcc_token.retrieve_opts()
```

# `t`
*since 3.0.0* 

```elixir
@type t() :: %Oidcc.Token{
  access: Oidcc.Token.Access.t() | none(),
  id: Oidcc.Token.Id.t() | none(),
  refresh: Oidcc.Token.Refresh.t() | none(),
  scope: :oidcc_scope.scopes()
}
```

# `client_credentials`
*since 3.0.0* 

```elixir
@spec client_credentials(
  client_context :: Oidcc.ClientContext.t(),
  opts :: :oidcc_token.client_credentials_opts()
) :: {:ok, t()} | {:error, :oidcc_token.error()}
```

Retrieve Client Credential Token

See https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.4

For a high level interface using `Oidcc.ProviderConfiguration.Worker`
see `Oidcc.client_credentials_token/4`.

## Examples

    iex> {:ok, pid} =
    ...>   Oidcc.ProviderConfiguration.Worker.start_link(%{
    ...>     issuer: "https://erlef-test-w4a8z2.zitadel.cloud"
    ...>   })
    ...>
    ...> {:ok, client_context} =
    ...>   Oidcc.ClientContext.from_configuration_worker(
    ...>     pid,
    ...>     System.fetch_env!("CLIENT_CREDENTIALS_CLIENT_ID"),
    ...>     System.fetch_env!("CLIENT_CREDENTIALS_CLIENT_SECRET")
    ...>   )
    ...>
    ...> {:ok, %Oidcc.Token{}} =
    ...>   Oidcc.Token.client_credentials(
    ...>     client_context,
    ...>     %{scope: ["openid"]}
    ...>   )

# `jwt_profile`
*since 3.0.0* 

```elixir
@spec jwt_profile(
  subject :: String.t(),
  client_context :: Oidcc.ClientContext.t(),
  jwk :: JOSE.JWK.t(),
  opts :: :oidcc_token.jwt_profile_opts()
) :: {:ok, t()} | {:error, :oidcc_token.error()}
```

Retrieve JSON Web Token (JWT) Profile Token

See https://datatracker.ietf.org/doc/html/rfc7523#section-4

For a high level interface using `Oidcc.ProviderConfiguration.Worker`
see `Oidcc.jwt_profile_token/6`.

## Examples

    iex> {:ok, pid} =
    ...>   Oidcc.ProviderConfiguration.Worker.start_link(%{
    ...>     issuer: "https://erlef-test-w4a8z2.zitadel.cloud"
    ...>   })
    ...>
    ...> {:ok, client_context} =
    ...>   Oidcc.ClientContext.from_configuration_worker(
    ...>     pid,
    ...>     "JWT Profile Test",
    ...>     "client_secret"
    ...>   )
    ...>
    ...> %{"key" => key, "keyId" => kid, "userId" => subject} = "JWT_PROFILE"
    ...>   |> System.fetch_env!()
    ...>   |> JOSE.decode()
    ...>
    ...> jwk = JOSE.JWK.from_pem(key)
    ...>
    ...> {:ok, %Oidcc.Token{}} =
    ...>   Oidcc.Token.jwt_profile(
    ...>     subject,
    ...>     client_context,
    ...>     jwk,
    ...>     %{scope: ["openid", "urn:zitadel:iam:org:project:id:zitadel:aud"], kid: kid}
    ...>   )

# `refresh`
*since 3.0.0* 

```elixir
@spec refresh(
  refresh_token :: String.t(),
  client_context :: Oidcc.ClientContext.t(),
  opts :: :oidcc_token.refresh_opts()
) :: {:ok, t()} | {:error, :oidcc_token.error()}
@spec refresh(
  token :: t(),
  client_context :: Oidcc.ClientContext.t(),
  opts :: :oidcc_token.refresh_opts_no_sub()
) :: {:ok, t()} | {:error, :oidcc_token.error()}
```

Refresh Token

For a high level interface using `Oidcc.ProviderConfiguration.Worker`
see `Oidcc.refresh_token/5`.

## Examples

    iex> {:ok, pid} =
    ...>   Oidcc.ProviderConfiguration.Worker.start_link(%{
    ...>     issuer: "https://api.login.yahoo.com"
    ...>   })
    ...>
    ...> {:ok, client_context} =
    ...>   Oidcc.ClientContext.from_configuration_worker(
    ...>     pid,
    ...>     "client_id",
    ...>     "client_secret"
    ...>   )
    ...>
    ...> # Get refresh_token from redirect
    ...> refresh_token = "refresh_token"
    ...>
    ...> Oidcc.Token.refresh(
    ...>   refresh_token,
    ...>   client_context,
    ...>   %{expected_subject: "sub"}
    ...> )
    ...> # => {:ok, %Oidcc.Token{}}

# `retrieve`
*since 3.0.0* 

```elixir
@spec retrieve(
  auth_code :: String.t(),
  client_context :: Oidcc.ClientContext.t(),
  opts :: retrieve_opts()
) :: {:ok, t()} | {:error, :oidcc_token.error()}
```

retrieve the token using the authcode received before and directly validate
the result.

the authcode was sent to the local endpoint by the OpenId Connect provider,
using redirects

For a high level interface using `Oidcc.ProviderConfiguration.Worker`
see `Oidcc.retrieve_token/5`.

## Examples

    iex> {:ok, pid} =
    ...>   Oidcc.ProviderConfiguration.Worker.start_link(%{
    ...>     issuer: "https://api.login.yahoo.com"
    ...>   })
    ...>
    ...> {:ok, client_context} =
    ...>   Oidcc.ClientContext.from_configuration_worker(
    ...>     pid,
    ...>     "client_id",
    ...>     "client_secret"
    ...>   )
    ...>
    ...> # Get auth_code from redirect
    ...> auth_code = "auth_code"
    ...>
    ...> Oidcc.Token.retrieve(
    ...>   auth_code,
    ...>   client_context,
    ...>   %{redirect_uri: "https://my.server/return"}
    ...> )
    ...> # => {:ok, %Oidcc.Token{}}

# `validate_id_token`
*since 3.0.0* 

```elixir
@spec validate_id_token(
  id_token :: String.t(),
  client_context :: Oidcc.ClientContext.t(),
  nonce_or_opts :: String.t() | :any | retrieve_opts()
) :: {:ok, :oidcc_jwt_util.claims()} | {:error, :oidcc_token.error()}
```

Validate ID Token

Usually the id token is validated using `retrieve/3`.
If you get the token passed from somewhere else, this function can validate it.

## Examples

    iex> {:ok, pid} =
    ...>   Oidcc.ProviderConfiguration.Worker.start_link(%{
    ...>     issuer: "https://api.login.yahoo.com"
    ...>   })
    ...>
    ...> {:ok, client_context} =
    ...>   Oidcc.ClientContext.from_configuration_worker(
    ...>     pid,
    ...>     "client_id",
    ...>     "client_secret"
    ...>   )
    ...>
    ...> #Get IdToken from somewhere
    ...> id_token = "id_token"
    ...>
    ...> Oidcc.Token.validate_id_token(id_token, client_context, :any)
    ...> # => {:ok, %{"sub" => "sub", ... }}

# `validate_jarm`
*since 3.2.0* 

```elixir
@spec validate_jarm(
  response :: String.t(),
  client_context :: Oidcc.ClientContext.t(),
  opts :: :oidcc_token.validate_jarm_opts()
) :: {:ok, :oidcc_jwt_util.claims()} | {:error, :oidcc_token.error()}
```

Validate the JARM response, returning the valid claims as a map.

the response was sent to the local endpoint by the OpenId Connect provider,
using redirects

## Examples

    iex> {:ok, pid} =
    ...>   Oidcc.ProviderConfiguration.Worker.start_link(%{
    ...>     issuer: "https://api.login.yahoo.com"
    ...>   })
    ...>
    ...> {:ok, client_context} =
    ...>   Oidcc.ClientContext.from_configuration_worker(
    ...>     pid,
    ...>     "client_id",
    ...>     "client_secret"
    ...>   )
    ...>
    ...> # Get auth_code from redirect
    ...> response = "JWT"
    ...>
    ...> Oidcc.Token.validate_jarm(
    ...>   response,
    ...>   client_context,
    ...>   %{}
    ...> )
    ...> # => {:ok, %{"code" => auth_code}}

# `validate_jwt`
*since 3.0.0* 

```elixir
@spec validate_jwt(
  jwt :: String.t(),
  client_context :: Oidcc.ClientContext.t(),
  opts :: :oidcc_token.validate_jwt_opts()
) :: {:ok, :oidcc_jwt_util.claims()} | {:error, :oidcc_token.error()}
```

Validate JWT

Validates a generic JWT (such as an access token) from the given provider.
Useful if the issuer is shared between multiple applications, and the access token
generated for a user at one client is used to validate their access at another client.

Validating an arbitrary JWT token (not an ID token) is not covered by the OpenID
Connect specification. Therefore the signing / encryption algorithms are not
derieved from the provider configuration, but must be provided by the caller.

## Examples

    iex> {:ok, pid} =
    ...>   Oidcc.ProviderConfiguration.Worker.start_link(%{
    ...>     issuer: "https://api.login.yahoo.com"
    ...>   })
    ...>
    ...> {:ok, client_context} =
    ...>   Oidcc.ClientContext.from_configuration_worker(
    ...>     pid,
    ...>     "client_id",
    ...>     "client_secret"
    ...>   )
    ...>
    ...> # Get JWT from Authorization header
    ...> jwt = "jwt"
    ...>
    ...> opts = %{
    ...>   signing_algs: client_context.provider_configuration.id_token_signing_alg_values_supported
    ...> }
    ...>
    ...> Oidcc.Token.validate_jwt(jwt, client_context, opts)
    ...> # => {:ok, %{"sub" => "sub", ... }}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
