oauth-2.0identityserver4openid-connectjwtpingfederate

OIDC - Obtaining an Identity Token for use by a backend (no actual user) service running scheduled jobs


We have an API (Actually several micro-services) secured by OIDC. The authorization server is owned and managed by our customers (not internal to us) and provides our SPA with an identity token. That SPA then passes that identity token to our backend server which validates the token, extracts the SubjectId (user), and then looks up their roles in an internal database. We are not using the token for authorization (meaning we ignore the claims), it is for authentication only.

We have a backend Windows Service that runs in a secure environment (so it can safely store secrets) that needs to call that same API. In order to call the API, it needs an OIDC Identity token to provide authentication. What is the best way to do that securely?

Diagram of the System

We have looked at these options:

  1. Username/Password Flow (OIDC) - We rejected this as it is deprecated. That makes it not a solid option for new code, but also, we can't be sure that our customers will allow its use now and in the future.
  2. Client Credentials Flow (OIDC) - We tried this... but it only provides an access token without identity token. Our whole requirement is an Identity token (As we use it to find the roles in our system)... so this doesn't seem to be an option.
  3. I looked at this article: https://nordicapis.com/how-to-handle-batch-processing-with-oauth-2-0/ which was interesting. I could set up a non expiring (or really long) identity token limited to one flow or the other... but requires me to host and OIDC server or manage theirs. Our customers won't give access.
  4. Martin Fowler wrote an article on using Refresh Tokens for this purpose: https://martinfowler.com/articles/command-line-google.html Two issues with this: It is for Auth not OIDC... so it doesn't deal with identity token. Should work, but curious if it has been done. Also, in my case, there is no person to refresh the refresh token when it expires... I need something that either doesn't expire or can be refreshed automatically by the backend services.

What is the best option?


Solution

  • Let me start by saying, I wouldn't recommend the architecture you have. So, I'll give you two answers: a short-term practical one and a medium-term one that will result in a more ideal system.

    First, the quick fix: turn on basic auth in the API. Then, call your API from the scheduled task runner using basic auth and a long secret, like this:

    Authorization: basic bXktZ29vZC1zZXJ2aWNlOkY2M0JEOTkyLTMzNTUtNDYzNi1CNzFBLTM3RDg0QzI3ODEzRQo=
    

    Then, keep validating the ID token you get from the SPA. I am assuming you're sending this to the API using the technique described in RFC 6750:

    Authorization: bearer eyJhbGciOiJSUzI1NiJ9.ey...
    

    So, these two different authentication methods make it easy for the API to tell who's calling it. What's more: it should be easy to implement, even with the authorization changes. This should hopefully allow you to put out the fire and then fix things on a more fundamental level.

    When you get to that point, I would suggest that you do two things:

    1. Add in your own token service (i.e., an authorization server). An API should only ever trust its own authorization server, not some foreign one. This will form a pivot point, which is what you need now and don't have.

    2. Only accept access tokens in your API. APIs should only consume access tokens. If an API accepts an ID token, there's a 99% chance it's doing things wrong.

    The SPA is the only one in your picture that needs an ID token. It should get that and an access token. What's more, it should get this from your OpenID Connect Provider (OP), not your customer's. This should do OpenID Connect to your customer, in turn, but will handle authentication, in general, so that your app can cope with other authentication requirements in the future without changes to the SPA. Also, other apps can reuse all this.

    The SPA (and the scheduling service) should send access tokens to the API. The token (or a phantom or split form of it) should contain at least the subject ID, so it can obtain more info needed to authorize the call. This should come from your token service (i.e., your new OP since it will do OAuth and OIDC).

    I would recommend the following high-level architectural documents and videos. These will help as you continue evolving your implementation: