GitHub OAuth application integration in Kratos

Hi,

I would like to add the Open ID Connect logic in kratos-selfservice-ui-node but I struggle to have a functional setup.

Is there any public functional setup somewhere?

I would like to have a github-identity.traits.schema.json to showcase an OIDC integration.

I struggle here and there in kratos/selfservice/strategy/oidc/strategy.go

here, is the schema validation from the third party provider.
there, is the identify schema validation.

For some reason, at first trial it fails to create a new identity and the request item ask to display a trait.email field in the registration form.
With an email address I manage to get an identity created.

Many thanks,

The schema_url of github provider

{
  "$schema": "I-cant-add-link#",
  "$id": "I-cant-add-links",
  "type": "object",
  "title": "The Root Schema",
  "description": "The root schema comprises the entire JSON document.",
  "required": [
      "name",
      "email"
  ],
  "properties": {
    "name": {
      "$id": "#/properties/name",
      "type": "string",
      "title": "The Subject Schema",
      "description": "An explanation about the purpose of this instance.",
      "default": "",
      "examples": [
        "ok"
      ]
    },
    "email": {
      "$id": "#/properties/traits/email",
      "type": "string",
      "format": "email",
      "title": "E-Mail",
      "minLength": 3
    }
  },
  "additionalProperties": true
}

Hi, so this works but isn’t documented yet.

GitHub doesn’t return the email address unless you specify the right scope. Because the email is missing, you will see the registration form where the user needs to enter the email manually. The same would apply if you had another field, like Terms Of Service that wasn’t clicked yet. This helps streamline the sign up process so that all identities have valid data according to your schema!

We have a config for this that we use for the cloud service we’re building atm, it looks like this:


selfservice:
  strategies:
    password:
      enabled: true
    oidc:
      enabled: true
      config:
        providers:
        - id: github
          provider: github
          client_id: ...
          client_secret: ...
          schema_url: https://.../projects/ory/schemas/oidc/github.schema.json
          scope:
          - user:email

Thanks @hackerman!

You would mind sharing your schema_url: https://.../projects/ory/schemas/oidc/github.schema.json?

There is one thing I don’t understand yet: do schema URL allow us to reformat third party payloads to fit into our schema?

Thanks again.

Yup, exactly!

Sure! This is it:

{
  "$id": "https://example.com/social.hydra.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "hive": {
        "mappings": {
          "identity": {
            "traits": [
              {
                "path": "email"
              }
            ]
          }
        }
      }
    },
    "name": {
      "type": "string",
      "hive": {
        "mappings": {
          "identity": {
            "traits": [
              {
                "path": "name"
              }
            ]
          }
        }
      }
    }
  }
}

(please note that we’re still using the hive annotation here, this should be changed to ory.sh/kratos iirc)

The identity itself:

{
  "$id": "https://example.com/person.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Person",
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "format": "email",
      "title": "E-Mail",
      "hive": {
        "credentials": {
          "password": {
            "identifier": true
          }
        }
      }
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "title": "Name"
    },
    "consent": {
      "type": "object",
      "title": "Consent",
      "properties": {
        "newsletter": {
          "type": "boolean",
          "title": "Newsletter subscription"
        },
        "tos": {
          "const": true,
          "title": "Terms of Service"
        }
      },
      "required": [
        "tos"
      ]
    }
  },
  "required": [
    "email",
    "consent",
    "name"
  ],
  "additionalProperties": false
}

I currently trying to setup a similar flow using a couple different OIDC providers to test, in my case both Github (shown above) and Auth0. I’ve managed to get both routing to the provider pages for credentials and successfully hitting a callback flow, but the flow seems to get stuck processing the OIDC callback with no errors in the logs and nothing in the UI, console, or network tabs to show for it. I have github, identity, and kratos config files near identical to the ones you showed above, and I get stuck on a callback URL that looks like this with an entirely empty webpage:

http://auth.localhost/self-service/browser/flows/registration/strategies/oidc/callback/github?code=<code>&state=<state-uuid>

I arrive at this state both for the github provider and the Auth0 provider. Is this a known issue with the OIDC flows (didn’t see one open on github), or is there some other configuration needed for it to work?

On an unrelated note - what was the rationale for request methods to be grouped exclusively into password vs. oidc when formatting the login requests? I went to update the ui-node example to render all configured login options as follows, and was surprised that the request methods didn’t give me fields/actions/errors for my auth0 provider separately from the github one:

    .then((request?: LoginRequest | RegistrationRequest) => {
      if (!request) {
        res.redirect(`${config.kratos.browser}/self-service/browser/flows/${type}`)
        return
      }

      // I expected this to have a different method for both "github" and "auth0" to help me build a more dynamic frontend
      const forms = Object.keys(request.methods).map(method => ({
        formFields: request.methods[method].config?.fields.sort(sortFormFields),
        formAction: request.methods[method].config?.action,
        errors: request.methods[method].config?.errors,
      }))

      res.render(type, {
        forms
      })
    })

– EDIT –

Once again, I figured out what I was doing wrong. In this case, I hadn’t registered any after login/registration jobs for the oidc method. After copying what I had for login/registration for the password
method, everything worked fine:

login:
    request_lifespan: 10m
    after:
      oidc:
        - job: session
        - job: redirect
          config:
            default_redirect_url: http://auth.localhost/
            allow_user_defined_redirect: true
      password:
        - job: session
        - job: redirect
          config:
            default_redirect_url: http://auth.localhost/
            allow_user_defined_redirect: true

Sorry for the late reply! So that means you got everything working?

We’re still working on figuring out a way to make the hooks easier to configure and make it harder to forget to set up e.g. the redirect.

I believe this is fixed on master but not sure. Could you please create an issue?