Skip to content
v2.18.0GitHub

OIDC client authentication

Let workloads on Kubernetes, Azure, GitLab CI or a tailnet authenticate as OIDC clients with tokens from their platform instead of a client secret.

A confidential client proves who it is when it calls the token endpoint, usually with its client secret. A secret is shared between the app and Pocket ID, so it has to be stored, rotated and kept from leaking, which is where most of its risk lies.

With federated client credentials, the client proves who it is with a token that its own platform issued instead, such as a Kubernetes service account token. Pocket ID trusts that token when its issuer, audience and subject match what you configured, so no long-lived secret exists at all:

Workload issuerKubernetes, GitLab, Azure…Your appthe OIDC clientPocket IDrequests a tokensigned JWTclient_assertion = the JWTsigning keys from the JWKS URLchecks iss, aud and subaccess token
The workload proves who it is with a token its platform issued. Pocket ID checks that token against the issuer, audience and subject you configured, so no client secret is stored anywhere.

The app needs to support JWT client assertions. Instead of client_secret, it sends these two parameters to the token endpoint:

client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion=<token from the platform>

Open the client under Administration → OIDC Clients, go to its Credentials tab and click Add Federated Client Credential:

Field Description
Issuer The iss claim of the platform’s tokens. Required.
Subject The sub claim of the tokens. Defaults to the client ID.
Audience The aud claim of the tokens. Defaults to your APP_URL.
Signing keys Where Pocket ID gets the keys to verify the tokens: a JWKS URL, which defaults to <issuer>/.well-known/jwks.json, or Public keys you paste in. Use HTTPS for the JWKS URL.
Replay Protection Accepts each token only once. Turn it off if the platform hands out the same token several times.

A client can have several federated credentials, such as one per environment. The platforms below each need slightly different values.

Using Kubernetes 1.21 or higher, you can use Projected Token Volumes to have the Kubernetes API server issue a token for the audience of your choice, and make it available to your app as projected volume.

Configuration values for using Kubernetes are:

  • Issuer: Value of the Kubernetes’ API server’s issuer (this is generally passed as the value of the --service-account-issuer flag for kube-apiserver).
  • Audience: Value of the audience option specified when creating the Service Account for the Pod. While you can set this to any value, a good option is to use the public URL of Pocket ID.
  • Subject: The value is in the format system:serviceaccount:<namespace>:<service-account-name>. E.g. for a ServiceAccount resource named my-sa in the namespace myappns, the value is system:serviceaccount:myappns:my-sa.
  • JWKS URL (optional): The URL where the JWKS of the Kubernetes API server can be retrieved from. The default value is <issuer>/.well-known/jwks.json.

Inside your application, you can obtain a JWT token to use as client assertion by reading the file where the projected token volume is mounted.

Additional resources:

On Microsoft Azure, you can use Microsoft Entra Workload ID (e.g. Managed Identity or Workload Identity) to federate with Pocket ID.

Set up steps for Azure:

  1. Assign an identity to your application, such as a System-assigned or User-assigned Identity. Instructions are specific to each service being used.
    • For workloads running on Azure Kubernetes Service, you may want to use Workload Identity
  2. Create an application in Microsoft Entra ID (docs)
    • Take note of the client ID of this app, which will be a UUID
    • Configure the Entra ID app with Federated credentials for the Managed Identity created for your resource (docs)

Configuration values for Federated Client Credentials in Pocket ID:

  • Issuer: https://sts.windows.net/<tenant-id>/ where <tenant-id> is the UUID of your Microsoft Entra ID tenant
  • Audience: The client ID of the Entra ID application created above
  • Subject: The object ID of the managed identity (note: this is the object (or principal) ID, not a client ID)
  • JWKS URL: Constant value https://login.microsoftonline.com/common/discovery/keys

Inside your application, you can obtain a token from the Managed Identity by:

  • Recommended: using one of the Azure SDKs to get a token from Managed Identity, with the requested resource as the client ID of the Entra ID application. SDKs work on all Azure services automatically.
  • Manually invoking the endpoint metadata service. The endpoint can be different depending on the Azure service; in the case of an Azure Virtual Machine, the URL is http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=<client-id> (where <client-id> is the client ID of the Entra ID application); make sure to also set the HTTP header Metadata:true in the request.

If your application is running on a node that is joined to a Tailscale network (“tailnet”), you can use the third-party tsiam application to provide workload identity.

When requesting a workload identity token from tsiam, it’s recommended to set as resource the endpoint of Pocket ID, for example https://pocketid.example.com

Configuration values for Federated Client Credentials in Pocket ID:

  • Issuer: The URL of your tsiam instance inside the tailnet, for example https://tsiam.tail<tailnet-id>.ts.net
  • Audience: The value of the resource used when requesting a token from tsiam; recommended to use the endpoint of Pocket ID (e.g. https://pocketid.example.com)
  • Subject: The full name of the node in the tailnet, e.g. <node-name>.tail<tailnet-id>.ts.net
  • JWKS URL: Leave empty to use the default value

When running jobs in GitLab Pipelines, your job can authenticate as an OIDC client with Pocket ID using GitLab ID tokens.

Configuration values for Federated Client Credentials in Pocket ID:

  • Issuer: https://gitlab.com (or your self-hosted GitLab domain)
  • Audience: https://pocketid.example.com - recommended to use the Pocket ID endpoint as the audience.
  • Subject: project_path:my-group/my-project:ref_type:branch:ref:main - replace my-group/my-project with the project you will be running the pipeline on. If working on a branch, change main to your branch name. Currently, wildcards are not supported, so if you need to authenticate from pipelines running on different branches, you will need to create a Federated Client Credential for each branch.
  • JWKS URL: https://gitlab.com/oauth/discovery/keys - if self-hosting, replace gitlab.com.

Here’s an example GitLab job that authenticates as an OIDC client:

.gitlab-ci.yml
example-job:
image: alpine:3.23.3
id_tokens:
GL_PIPELINE_TOKEN:
aud: "$POCKET_ID_URL" # must match the `Audience` configured in Pocket ID.
script:
- apk update
- apk add --no-cache curl jq
- responsefile=$(mktemp)
- |
curl -SsfX POST --url "$POCKET_ID_URL/api/oidc/token" \
-F "grant_type=client_credentials" \
-F "client_id=$POCKET_ID_CLIENT_ID" \
-F "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-F "client_assertion=$GL_PIPELINE_TOKEN" \
-o "$responsefile"
- access_token=$(jq '.access_token' -r < "$responsefile")
- echo "Successfully obtained token with subject: client-$POCKET_ID_CLIENT_ID"

The only variables you need to define are:

  • POCKET_ID_URL: The base URL of your pocket ID instance, such as https://pocketid.example.com. In the job above this URL is also used as the Audience of the Federated Client Credential.
  • POCKET_ID_CLIENT_ID: the client ID of the OIDC Client configured in Pocket ID.

In the job above the client_credentials grant type is used, which means that the final token will have a subject of the form client-[client_id] (as opposed to a uuid identifying a user).