Hi team. Quick question. Does Ory have any capabil...
# general
e
Hi team. Quick question. Does Ory have any capabilities for user api keys? For example generate an api key for a user so that they can access my backend APIs programmatically? IM assuming I can generate and hash api keys on my backend and let the user know to save them as they wonโ€™t be shown again. Then configure oathkeeper to validate the bearer token header on my backend when api calls are made but I wanted to see if there are any better ways.
s
What you suggested is the way to go. There have been ideas for API keys, but it is not even on the backlog yet.
e
@steep-lamp-91158 awesome! thanks for validating
r
I just saw this thread so I figured I share what we came up with: We are exploring a simple service that creates full blown JWTs on the backend, that we can attach permissions/scopes etc. to. Because of the problems with JWTs (in regards to invalidation and complexity), we expose an intermediate value to the user which is then validated via a custom backend via oathkeeper. That intermediate value is a hash string (can be anything), just has to be unique enough across all users. Since we're using bearer and session (for Kratos) flows in oathkeeper already, we decided to fake an "oauth2_introspection" flow โ€” for lack of time of building something into oathkeeper (also because, not sure where oathkeeper is at in terms of v1 and v2). For the "routing" in Oathkeeper, it seems to be necessary to come up with a different header field, so e.g.
X-Token
So in the end we can have requests against oathkeeper with an api token value, that value is validated by the (fake oauth2_introspection) endpoint and the configured flow. The return value in our case is something like,
{"user":"xxx", "settings": "..."}
which oathkeeper adds to the request and passes it on to our backends. Not sure if there's anything better? ๐Ÿ˜„
s
We're using a similar approach for our internal API keys, this is the relevant oathkeeper config part:
Copy code
bearer_token: {
      enabled: true,
      config: {
        prefix: 'ory_pat_',
        check_session_url: '<https://backend>',
        subject_from: 'id',
        preserve_path: true,
        preserve_host: false,
        // We need to force POST here because on HEAD requests the body is not sent & received.
        force_method: 'POST',
        // We add some extra info for later steps here.
        extra_from: '[@this,{"namespace":!"ApiKey"}].@join',
      },
    },
Not sure if the bearer token prefix is available on the latest release or only when you build from master though. Maybe this can help you somehow still.
r
I had to use oath2_introspection because we already use bearer and you cannot have multiple
s
You can when you define it as part of the rule, but that can be a lot of copy-paste.
r
@steep-lamp-91158 can you share an example maybe? I've searched the docs before, couldn't find anything. I'll check out master later too.
s
This could be one such rule
Copy code
{
      id: '...',
      match: {
        methods: [
          'GET',
          'HEAD',
          'POST',
          'PUT',
          'PATCH',
          'DELETE',
        ],
        url: '...',
      },
      authenticators: [
        {
          handler: 'cookie_session',
        },
        {
          handler: 'bearer_token',  // API key type 1
          config: {
            prefix: 'api_key_1_',
            check_session_url: '...',
            subject_from: '...',
            preserve_path: true,
            preserve_host: true,
            // We need to force POST here because on HEAD requests the body is not sent & received.
            force_method: 'POST',
            extra_from: '[@this,{"namespace":!"ApiKey"}].@join',
          },
        },
        {
          handler: 'bearer_token',  // API key type 2
          config: {
            prefix: 'api_key_2_',
            check_session_url: '....',
            subject_from: '...',
            preserve_path: true,
            preserve_host: true,
            // We need to force POST here because on HEAD requests the body is not sent & received.
            force_method: 'POST',
            extra_from: '[@this,{"namespace":!"ApiKey"}].@join',
          },
        },
        {
          handler: 'oauth2_introspection',
        },
      ],
      authorizer: {
        handler: 'allow',
      },
      mutators: [
        {
          handler: 'id_token',
        },
      ],
      upstream: {
        ....
      },
    },
r
Thank you, that's very helpful! ๐Ÿ™‚