Social Login (OIDC / OAuth 2.0)¶
VibeWarden delegates social login entirely to Ory Kratos.
Kratos supports every major OAuth 2.0 / OIDC provider through its oidc self-service
method. This document shows you how to configure each supported provider.
How the redirect URL is constructed¶
Every OAuth provider requires you to register a callback (redirect) URL. The URL always follows this pattern:
<provider-id> is the id field you set in the Kratos oidc method config.
VibeWarden proxies the /self-service/ path prefix directly to Kratos, so the
callback URL above resolves correctly through VibeWarden.
Examples (replace your-domain with your actual domain):
| Provider | Callback URL |
|---|---|
https://your-domain/self-service/methods/oidc/callback/google |
|
| GitHub | https://your-domain/self-service/methods/oidc/callback/github |
| Apple | https://your-domain/self-service/methods/oidc/callback/apple |
https://your-domain/self-service/methods/oidc/callback/facebook |
|
| Microsoft | https://your-domain/self-service/methods/oidc/callback/microsoft |
| Generic OIDC | https://your-domain/self-service/methods/oidc/callback/<your-id> |
Where the config lives¶
Social providers are configured in your Kratos config file (kratos.yml),
not in vibewarden.yaml. VibeWarden reads no provider credentials directly —
it simply proxies traffic to Kratos.
The general structure in kratos.yml:
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: google # must match the callback URL segment
provider: google
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
scope:
- email
- profile
mapper_url: "base64://<base64-encoded-jsonnet>"
Kratos expands ${ENV_VAR} syntax at startup, so credentials are never stored
in the config file.
Google¶
1. Create OAuth 2.0 credentials¶
- Open Google Cloud Console and select your project (or create one).
- Go to APIs & Services > Credentials.
- Click Create Credentials > OAuth client ID.
- Choose Web application as the application type.
- Under Authorized redirect URIs add:
- Click Create. Copy the Client ID and Client secret.
2. Environment variables¶
3. Kratos config snippet¶
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: google
provider: google
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
scope:
- email
- profile
mapper_url: "base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dEZpbGUoInBheWxvYWQuanNvbiIpOwp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgICBuYW1lOiBjbGFpbXMubmFtZSwKICAgIH0sCiAgfSwKfQ=="
The mapper_url value above is a base64-encoded Jsonnet snippet
that maps the provider's claims to Kratos identity traits. Decode it to inspect or
customise the mapping:
A minimal mapper that copies email and name:
local claims = std.extVar("claims");
{
identity: {
traits: {
email: claims.email,
name: claims.name,
},
},
}
GitHub¶
1. Create an OAuth App¶
- Go to GitHub Settings > Developer settings > OAuth Apps.
- Click New OAuth App.
- Fill in the form:
- Application name: any name (e.g. "MyApp via VibeWarden")
- Homepage URL: your app's public URL
- Authorization callback URL:
- Click Register application.
- On the next page, copy the Client ID and generate a Client secret.
2. Environment variables¶
3. Kratos config snippet¶
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: github
provider: github
client_id: ${GITHUB_CLIENT_ID}
client_secret: ${GITHUB_CLIENT_SECRET}
scope:
- user:email
mapper_url: "base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dEZpbGUoInBheWxvYWQuanNvbiIpOwp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgfSwKICB9LAp9"
Note: request the user:email scope so that GitHub includes the user's email
in the token claims. GitHub does not expose the email by default when it is set
to private.
Apple¶
Apple Sign In has additional requirements compared to other providers.
1. Create a Services ID in Apple Developer¶
- Sign in to Apple Developer and go to Certificates, Identifiers & Profiles > Identifiers.
- Click + and select Services IDs, then click Continue.
- Enter a description and a unique Identifier (e.g.
dev.your-company.app.social). This becomes yourclient_id. - Click Continue, then Register.
- Back on the Identifiers list, click your new Services ID.
- Enable Sign In with Apple, then click Configure.
- Add your domain under Domains and Subdomains and add the callback URL under Return URLs:
- Click Save, then Continue, then Register.
2. Create a private key¶
- Go to Keys in the Apple Developer portal.
- Click +, give the key a name, and enable Sign In with Apple.
- Click Configure and select your primary App ID.
- Click Save, then Continue, then Register.
- Download the
.p8key file. This is the only time you can download it. - Note the Key ID shown on the confirmation page.
3. Gather required values¶
| Value | Where to find it |
|---|---|
client_id |
The Services ID Identifier you created (e.g. dev.your-company.app.social) |
team_id |
Your 10-character Apple Developer Team ID, shown in the top-right of the developer portal |
key_id |
The Key ID from the key you downloaded |
private_key |
Contents of the .p8 file (the private key itself, not the file path) |
4. Environment variables¶
APPLE_CLIENT_ID=dev.your-company.app.social
APPLE_TEAM_ID=XXXXXXXXXX
APPLE_KEY_ID=YYYYYYYYYY
# Inline the .p8 contents, preserving newlines:
APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----"
5. Kratos config snippet¶
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: apple
provider: apple
client_id: ${APPLE_CLIENT_ID}
apple_team_id: ${APPLE_TEAM_ID}
apple_private_key_id: ${APPLE_KEY_ID}
apple_private_key: ${APPLE_PRIVATE_KEY}
scope:
- name
- email
mapper_url: "base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dEZpbGUoInBheWxvYWQuanNvbiIpOwp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgfSwKICB9LAp9"
Why apple_team_id and apple_private_key_id? Apple does not issue a standard
client_secret. Instead, Kratos generates a short-lived JWT signed with your private
key. The team_id and key_id fields are used to construct the JWT header and claims
as specified in Apple's documentation.
Facebook¶
1. Create an App in Meta Developer Portal¶
- Go to Meta for Developers and click My Apps > Create App.
- Choose Consumer as the use case and click Next.
- Enter an app name and click Create App.
- In the app dashboard, find Facebook Login and click Set up.
- Choose Web as the platform.
- Under Facebook Login > Settings, add the callback URL to Valid OAuth Redirect URIs:
- Click Save Changes.
- Go to Settings > Basic and copy the App ID and App Secret.
2. Environment variables¶
3. Kratos config snippet¶
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: facebook
provider: facebook
client_id: ${FACEBOOK_CLIENT_ID}
client_secret: ${FACEBOOK_CLIENT_SECRET}
scope:
- email
- public_profile
mapper_url: "base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dEZpbGUoInBheWxvYWQuanNvbiIpOwp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgfSwKICB9LAp9"
Note: Facebook requires your app to pass a review before the email permission
is available for users outside your developer team. For testing, add test users
in the Meta Developer Portal under Roles > Test Users.
Microsoft (Azure AD)¶
1. Register an app in Azure AD¶
- Open Azure Portal and navigate to Azure Active Directory > App registrations.
- Click New registration.
- Enter a name for the app.
- Under Supported account types, choose the appropriate option:
- Single tenant — only users in your organisation
- Multitenant — users from any Azure AD organisation
- Multitenant + personal Microsoft accounts — broadest support
- Under Redirect URI, select Web and enter:
- Click Register.
- On the overview page, copy the Application (client) ID and Directory (tenant) ID.
- Go to Certificates & secrets > Client secrets > New client secret. Set an expiry, click Add, and copy the Value (you cannot retrieve it later).
2. Environment variables¶
MICROSOFT_CLIENT_ID=<application-client-id>
MICROSOFT_CLIENT_SECRET=<client-secret-value>
MICROSOFT_TENANT=<directory-tenant-id>
# Use "common" for multitenant + personal accounts,
# or your tenant ID for single-tenant.
3. Kratos config snippet¶
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: microsoft
provider: microsoft
client_id: ${MICROSOFT_CLIENT_ID}
client_secret: ${MICROSOFT_CLIENT_SECRET}
microsoft_tenant: ${MICROSOFT_TENANT}
scope:
- profile
- email
- openid
mapper_url: "base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dEZpbGUoInBheWxvYWQuanNvbiIpOwp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgfSwKICB9LAp9"
Set microsoft_tenant to common to allow both organisational and personal
Microsoft accounts, or to your specific tenant ID to restrict login to your
organisation only.
Generic OIDC¶
Any identity provider that exposes an OIDC discovery document
(i.e. <issuer>/.well-known/openid-configuration) can be configured using
Kratos's generic provider. This covers Keycloak, Auth0, Okta, Dex, GitLab,
and any other standards-compliant IdP.
1. Register a client in your IdP¶
The exact steps depend on your IdP, but you will always need to:
- Create an OAuth 2.0 / OIDC client (also called an "application").
- Set the redirect / callback URI to:
Replace
<your-provider-id>with theidyou will use in the Kratos config. - Note the client ID, client secret, and the issuer URL
(e.g.
https://sso.your-company.com/realms/myrealmfor Keycloak).
2. Environment variables¶
OIDC_CLIENT_ID=<client-id>
OIDC_CLIENT_SECRET=<client-secret>
OIDC_ISSUER_URL=https://sso.your-company.com/realms/myrealm
3. Kratos config snippet¶
selfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: my-oidc-provider # choose any slug; appears in the callback URL
provider: generic
client_id: ${OIDC_CLIENT_ID}
client_secret: ${OIDC_CLIENT_SECRET}
issuer_url: ${OIDC_ISSUER_URL}
scope:
- openid
- email
- profile
mapper_url: "base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dEZpbGUoInBheWxvYWQuanNvbiIpOwp7CiAgaWRlbnRpdHk6IHsKICAgIHRyYWl0czogewogICAgICBlbWFpbDogY2xhaW1zLmVtYWlsLAogICAgfSwKICB9LAp9"
Kratos fetches <issuer_url>/.well-known/openid-configuration automatically to
discover all endpoints, so you do not need to specify the authorization, token,
or userinfo endpoints manually.
Keycloak example¶
For a Keycloak realm named myrealm running at https://sso.example.com:
- id: keycloak
provider: generic
client_id: ${KEYCLOAK_CLIENT_ID}
client_secret: ${KEYCLOAK_CLIENT_SECRET}
issuer_url: https://sso.example.com/realms/myrealm
scope:
- openid
- email
- profile
mapper_url: "base64://..."
Callback URL to register in Keycloak:
Auth0 example¶
For an Auth0 tenant your-tenant.us.auth0.com:
- id: auth0
provider: generic
client_id: ${AUTH0_CLIENT_ID}
client_secret: ${AUTH0_CLIENT_SECRET}
issuer_url: https://your-tenant.us.auth0.com/
scope:
- openid
- email
- profile
mapper_url: "base64://..."
Callback URL to register in Auth0:
Enabling the OIDC self-service method in vibewarden.yaml¶
Add a social_providers block under the kratos section in your
vibewarden.yaml. This block tells VibeWarden which providers are active
so it can surface provider names in structured log events.
kratos:
public_url: "http://127.0.0.1:4433"
admin_url: "http://127.0.0.1:4434"
social_providers:
- google
- github
- microsoft
The actual OAuth credentials are always configured in kratos.yml, not here.
Claim mappers reference¶
Every provider entry requires a mapper_url. The mapper is a
Jsonnet snippet that transforms the provider's JWT claims
into Kratos identity traits.
You can supply the mapper as:
- base64 inline (
base64://<base64-encoded-jsonnet>) — simplest, no extra files. - file path (
file:///path/to/mapper.jsonnet) — easier to read and edit. - HTTP URL (
https://...) — useful for centralised multi-provider setups.
A mapper that works for most providers:
local claims = std.extVar("claims");
{
identity: {
traits: {
email: claims.email,
name: claims["name"],
},
},
}
Encode it for inline use:
Then prefix the result with base64:// in the config.
Troubleshooting¶
"redirect_uri_mismatch" — The callback URL registered with the provider does
not exactly match the URL Kratos sent. Check for trailing slashes, http vs
https, and that the provider id in the Kratos config matches the URL segment.
"invalid_client" — The client ID or secret is wrong, or the environment variable was not exported before starting Kratos.
"access_denied" — The user denied the consent screen, or the app has not been approved for the requested scopes by the provider (common with Facebook).
Kratos returns a 500 during the callback — Enable debug logging in
kratos.yml (log.level: debug) and inspect the Kratos container logs.
The most common cause is a misconfigured claim mapper (Jsonnet syntax error or
missing claim field).
Apple private key format errors — Ensure the APPLE_PRIVATE_KEY environment
variable preserves the literal newlines of the .p8 file. When using Docker
Compose, pass the variable through a .env file rather than inlining it in the
compose file to avoid YAML escaping issues.