Skip to main content

Social Sign In, OpenID Connect, and OAuth2

The oidc method uses OpenID Connect, or OAuth2 where OpenID Connect isn't supported, to authenticate identities using a third-party identity provider, such as Google, Microsoft, GitHub - or any other OAuth2 / OpenID Connect provider (for example Ory Hydra). "Social Sign In" or "Sign in with ..." are common aliases for this flow.

This strategy expects that you've set up your Default Identity Schema.

Configuration

You can configure multiple OAuth2 / OpenID Connect providers. First, enable the oidc method:

path/to/my/kratos/config.yml
# kratos -c path/to/my/kratos/config.yml serve
selfservice:
methods:
oidc:
enabled: true

Next, you need to configure the providers you want to use.

info

The most important configuration key is the provider's id. Once set, you should never remove or change that id. Otherwise, your existing users will no longer be able to sign in.

The provider configuration looks as follows:

path/to/my/kratos/config.yml
# kratos -c path/to/my/kratos/config.yml serve
selfservice:
methods:
oidc:
config:
providers:
- # REQUIRED - The ID of the provider.
# DO NOT change this once this is in use.
id: github

# REQUIRED - The provider you would like to use. Ory Kratos comes with some predefined providers to make
# life easier for you, but you can always opt for the "generic" provider, which works
# with any Certified OpenID Connect Provider (Google, Ory Hydra, ...):
#
# provider: github
# provider: google
# .... more to come
#
provider: generic

# REQUIRED - See section "Data Mapping with Jsonnet" for more information.
mapper_url: http://mydomain.com/github.schema.json

# The OAuth2 / OpenID Connect provider will provide you with a OAuth2 Client ID and Client Secret. You need
# to set them here:
client_id: ...
client_secret: ...

# What scope to request. Usually, this would be something like "profile" or "email".
# Please check the documentation of the OAuth2 / OpenID Connect provider to see what's allowed here.
scope:
- email

# issuer_url is the OpenID Connect Server URL. You can leave this empty if `provider` isn't set to `generic`.
# If set, neither `auth_url` nor `token_url` are required.
issuer_url: http://openid-connect-provider/

# auth_url is the authorize url, typically something like: https://example.org/oauth2/auth
# Should only be used when the OAuth2 / OpenID Connect server isn't supporting OpenID Connect Discovery and when
# `provider` is set to `generic`.
auth_url: http://openid-connect-provider/oauth2/auth

# token_url is the token url, typically something like: https://example.org/oauth2/token
# Should only be used when the OAuth2 / OpenID Connect server isn't supporting OpenID Connect Discovery and when
# `provider` is set to `generic`.
token_url: http://openid-connect-provider/oauth2/token

# requested_claims json object that specifies claims and optionally their properties which should
# be included in the id_token or returned from the UserInfo Endpoint.
#
# More information: https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
requested_claims:
userinfo:
given_name:
essential: true
nickname: null
email:
essential: true
email_verified:
essential: true
picture: null
http://example/info/claims/groups: null
id_token:
auth_time:
essential: true
acr:
values: ['urn:mace:incommon:iap:silver']
sub:
value: 248289761001
info

It's very important to add the "session" hook to the after oidc registration hooks. Otherwise your users need to use the login flow again to be able to get a session.

path/to/my/kratos/config.yml
# kratos -c path/to/my/kratos/config.yml serve
selfservice:
flows:
registration:
after:
oidc:
hooks:
- hook: session

Data Mapping with Jsonnet

The data provided by Google, GitHub, Facebook, and others will vary in payloads. One service might include the website or phone_number while another might not.

Therefore you need to specify how this data maps to the identity's traits. You can do that by writing a Jsonnet Code Snippet and referencing that in your Ory Kratos config file:

path/to/my/kratos/config.yml
# kratos -c path/to/my/kratos/config.yml serve
selfservice:
methods:
oidc:
config:
providers:
- id: github
# ...
mapper_url: file://path/to/my/kratos/github.data-mapper.jsonnet
# You can also load this from a HTTP(S) source:
mapper_url: https://domain.com/path/to/my/kratos/github.data-mapper.jsonnet
# Or inline it using base64 encoding:
mapper_url: base64://bG9jYWwgY2xhaW1zID0gc3RkLmV4dFZhcignY2xhaW1zJyk7CmlmIHN0ZC5sZW5ndGgoY2xhaW1zLnN1YikgPT0gMCB0aGVuCiAgZXJyb3IgJ2NsYWltIHN1YiBub3Qgc2V0JwplbHNlCiAgewogICAgaWRlbnRpdHk6IHsKICAgICAgdHJhaXRzOiB7CiAgICAgICAgZW1haWw6IGNsYWltcy5zdWIsCiAgICAgICAgW2lmICJ3ZWJzaXRlIiBpbiBjbGFpbXMgdGhlbiAid2Vic2l0ZSIgZWxzZSBudWxsXTogY2xhaW1zLndlYnNpdGUsCiAgICAgIH0sCiAgICB9LAogIH0=

Ory Kratos adds an external variable called claims to the data mapper. It contains all the claims such as username, email, and others for the OpenID Connect or OAuth2 Provider. Keep in mind that the claims will vary per provider and per flow - depending on what permissions the user grants you (for example "App XYZ can't see my private email"). Your Jsonnet code must return a JSON object that looks like:

{
identity: {
traits: {
/* ... */
}
}
}
note

For more information on Jsonnet check out our Jsonnet Documentation.

To debug Jsonnet payloads, use the --dev flag and set log.level to debug (for example LOG_LEVEL=debug kratos serve --dev). Logs with detailed payloads will be emitted once you complete an OpenID Connect / OAuth2 login or registration.

The Jsonnet code snippet

path/to/my/kratos/github.data-mapper.jsonnet
# claims contains all the data sent by the upstream.
local claims = std.extVar('claims');

{
identity: {
traits: {
email: claims.email, // If email isn't set the Jsonnet snippet will fail with an error.
[if "website" in claims then "website" else null]: claims.website, // The website claim is optional.
},
},
}

returns

{
"identity": {
"traits": {
"email": "foo@ory.sh",
"website": "https://www.ory.sh"
}
}
}

when the ID Token body (or the OAuth2 equivalent) returned by the OpenID Connect provider contains:

{
"sub": "some-identity-id-4hA8gk",
"email": "foo@ory.sh",
"website": "https://www.ory.sh"
}

which is then being used for the identity's traits.

The sub field, which is returned by OpenID Connect and OAuth2 servers alike is used as the primary credential identifier for the provider. This allows Ory Kratos to link the identity to the "social sign in profile" for future login flows:

# This is the YAML representation of an identity
id: '9f425a8d-7efc-4768-8f23-7647a74fdf13'

credentials:
oidc:
id: oidc
identifiers:
- example:some-identity-id-4hA8gk
config:
- provider: example
identifier: some-identity-id-4hA8gk

schema_url: http://foo.bar.com/person.schema.json # This comes from the default identity schema url.

traits:
email: foo@ory.sh # This is extracted from `username` using
website: https://www.ory.sh # This is extracted from `username` using

External Variable claims

The std.ExtVar('claims') object has the following structure and keys available:

package oidc

type Claims struct {
Issuer string `json:"iss,omitempty"`
Subject string `json:"sub,omitempty"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
LastName string `json:"last_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
Nickname string `json:"nickname,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
Profile string `json:"profile,omitempty"`
Picture string `json:"picture,omitempty"`
Website string `json:"website,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Gender string `json:"gender,omitempty"`
Birthdate string `json:"birthdate,omitempty"`
Zoneinfo string `json:"zoneinfo,omitempty"`
Locale string `json:"locale,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"`
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"`
HD string `json:"hd,omitempty"`
}

Identity Traits Validation and Data Completion

Sometimes the data provided by OpenID Connect or OAuth2 Providers isn't enough. A common example is asking the user to consent to the terms of service. No OpenID Connect or OAuth2 provider will be able to give you this information because these are your terms. Another example would be a user not agreeing to share his/her email address with you when authorizing your OAuth2 app. If such a validation error occurs, the user will be redirected to the Registration UI. The Registration Flow includes all the valid and invalid fields.

When submitting the form again, the data provided by the user and the data coming from the OpenID Connect / OAuth2 provider will be merged. This process repeats itself until the identity's traits are valid against the defined JSON Schema.

For more information on this flow (network flow, examples, UI, ...) head over to the OpenID Connect and OAuth2 Self-Service Method Documentation.