Scenario:
A controller process obtains a short-lived JWT access token and a refresh token handle using client credentials grant.
The controller spawns one or more isolated worker nodes and passes the access and refresh token to the node(s).
The child node may continue with a long running task where the access token may expire and a refresh needs to occur.
In a background thread, before expiration, the agent refreshes its token, the access token is regenerated. The need for short lived access tokens and a refresh lifecycle is to regenerate temporal JWT claims (claims that are time-transitive such as uris).
The child may return a response to the parent. Both the parent and children need to maintain an independent session validation lifecycle without invalidating the other.
Assumptions:
Assume each process is an atomic unit of work with independent state. The access token is authenticated once by the controller and passed down with its refresh token to N+1 or N*N worker nodes.
Any given N process may proactively regenerate its access token with the refresh token handed down to it to extend its lifetime and prevent expiration.
The process may run in a trusted (private) or untrusted (public) environment over HTTPS & SSL.
There is one authorization server and one api resource server.
Problem:
How would the agent validate both the access token and refresh token lifetime ensuring that any child or parent process can refresh its session during its lifetime without exposing any security attack vectors or invalidating another agent in the pool?
Mutual TLS client certificate authentication may be a good option for this. It involves parties authenticating each other through verifying the provided digital certificate so that both parties are assured of the others' identity. In technology terms, it refers to a client application authenticating themselves to a server and that server also authenticating itself to the client through verifying the public key certificate/digital certificate issued by the trusted Certificate Authorities (CAs).
Related
I have a web service that makes use of the Authorization Code grant type to get an Access Token for user data on a Resource Server, together with a Refresh Token. Now my web service launches jobs at certain points in time that should also access this user data. However,
there may be more than one job running at a time,
the user is not necessarily around when the jobs are spawned, and
the jobs may be long-running, in particular they may live longer than the validity of the Access Token.
My question is: How can I provide each job with an Access Token so that
no user interaction is required for every single job launch, and
each job can refresh its own Access Token if necessary, without invalidating other jobs' tokens?
While https://datatracker.ietf.org/doc/html/rfc6749#section-1.5 states that a refresh token can be used to obtain "additional access tokens" (emphasis mine), the spec is not so clear on whether the previous Access Token remains valid until it's expiry date (well, if it does not I wouldn't call it "additional"). Also, according to https://datatracker.ietf.org/doc/html/rfc6749#section-6 if a server issues a new Refresh Token to prevent replay attacks, then the old Refresh Token cannot be used any more, so now maybe I have an additional Access Token, but I can't really pass the Access Token and the new Refresh Token to the job, because if the job made use of that Refresh Token would then my web service couldn't use it any more.
Are there maybe any lesser-known flows that allow for this kind of unattended Access Token creation?
Normally access tokens and refresh tokens are valid till the exipration time.
Having multiple access and refresh tokens are also allowed.
However a refresh token can be revoked under following scenarios
the authorization server has revoked the refresh token
the user has revoked their consent for authorization
the refresh token has expired
the authentication policy for the resource has changed
Since you are having the background jobs running. I suggest not to use JWT Tokens for authentication. Instead you can have your custom security standards.
Like custom API Key, UserAgent for Jobs and you can pass the user information with the requset or as custom-header as well.
We are implementing JWT-based authentication for our APIs, using Keycloak as an identity provider and token endpoint.
We found the idea of OAuth2 attractive, as it reduces the need for long-lived credentials to travel over the network. Instead of issuing our end-users an API key which they would put into a header for every request they send our way, they can use a client ID and secret to request an access token with a relatively short lifespan.
I mistakenly assumed that the clients would also receive a refresh token and be able to get new access/refresh token pairs using that (and not using the client secret). This would further lessen the need for the client secret to go back and forth over the network.
Here's some background for my initial reasoning:
Client gets access/refresh tokens with client credentials
Client uses access token until expiry
Client uses refresh token to obtain a new access/refresh pair.
The old refresh token is invalidated (this is possible to do with Keycloak by tweaking max sessions).
Retrieving an access/refresh pair with static credentials similarly invalidates a refresh token.
In this case an attacker sniffing network traffic would be very lucky to ever see static credentials passed and the compromise of credentials (whether static or refresh) is very visible, since the legitimate client would notice that its refresh token no longer works.
However, from what I gathered in the standards, the generally accepted way for machine-to-machine interaction to happen under OAuth2 is with the client credential grant type, which does not return refresh tokens and the client must request a new access token with its credentials every 10-15 minutes or so.
This leaves me confused: is this not, basically, another convoluted type of basic auth then, where we repeatedly send static credentials over a potentially insecure network? Sure, most requests are done with an access token, but an attacker will know for sure that every 10-15 minutes the static credentials will be sent to the authorization server.
Is there another way to do machine-to-machine interaction which is better secured against an attacker, who has managed to sniff our traffic? To be more specific, something which avoids, or mostly avoids sending static credentials.
Regarding your question about another way to do machine-to-machine interaction which is better secured against an attacker, who has managed to sniff our traffic, there are alternative authentication methods that a client can use.
The default client auth method is based on a shared key, called client secret in OAuth 2.0 spec. Client id and secret must be sent to the auth server to obtain an access token. This has to happen every time previously issued access token expires. This increases the risk of client secret exposure. This method does not require a refresh token as the client is obtaining a token for itself. This method is performed over SSL which reduces the risk of exposure. In addition, active IP monitoring further reduces the risk of compromised client secret. Client secret can be invalidated if a request is issued from an IP that has not been seen.
However, a more secure alternative to a shared client key is a private key and a signed JWT authentication. A client uses a private key and signs a set of assertions in a JWT token. This JWT is sent with the token request. The JWT is extremely short lived with expiry set to a few seconds from the issue. The auth server is configured with a corresponding public key and is able to validate JWT signature this authenticating the client's request. This JWT is a single use token and should include a unique token id. Auth server can persist unique JWT id until expiry to prevent token replay.
In addition to a short lived JWT, client IP monitoring can be implemented so that the client is only allowed to use this key from an established set of IP addresses or from previously observed set of IP addresses.
OAuth 2.0 also provides for a client to authenticate via a X.509 certificate submitted to the auth server during TLS handshake.
https://datatracker.ietf.org/doc/html/rfc8705
We have a microservices environment using Identity Server 3. Identity is provided to http microservices via bearer tokens in the authorisation http header, where the token is a JWT. That JWT usually represents a logged in end user, but it can also sometimes represent a system user that has authenticated via client credentials flow.
Messages are published on a queue (RabbitMQ) by these microservices, to be processed asynchronously. Currently, we have a windows service which consumes those messages. It authenticates as a system user with client credentials, and sends that JWT in the auth header to other http microservices.
We would like to maintain the identity of the user that publishes the messages throughout the flow, including within the machine-to-machine (m2m) communication when a message is consumed from the queue and when that consumer calls other microservices. Ie, when Service A (which was provided with a JWT) publishes a message to the queue, then the windows service should be able to impersonate the user represented in Service A's JWT, and should be able to provide a JWT representing that same user when calling Service B.
Service A (running as alice) --> RMQ
RMQ <-- Win Service (running as alice) --> Service B (running as alice)
Only clients with the correct claim should be able to impersonate a user in this way.
Which flow should I use in order to return the JWT to the Windows Service and how should this be achieved in Identity Server 3? I've managed to generate the JWT using Resource Owner flow, passing in a dummy username and password (overriding AuthenticateLocalAsync), although I've not yet attempted to check that the Win Service's client has a valid claim to impersonate. Or should this be a custom flow, implementing ICustomGrantValidator? Perhaps client credentials flow can be used?
Note that the original JWT can be provided with the message, or just the user id itself. The original JWT may have expired, which is why the windows service has to re-authenticate in some way.
My understanding is you want to propagate authenticated identities through a distributed architecture that includes async messaging via RabbitMQ message broker.
When you send/publish messages to RabbitMQ you might consider including the JWT in the message headers i.e. similar to how JWTs are included in HTTP headers for calls to protected HTTP routes. Alternatively if you're feeling a bit lazy, you could just have the JWT directly on the message payload. Your async (Windows service) consumer could validate the JWT on it's way through or it might just pass it through on the subsequent HTTP requests to protected routes of 'other http microservices'.
I'm not sure if you're question about 'which flow should I use' is relevant as presumably the user is already authenticated (via one of the OIDC/authN flows e.g. authentication code grant, implicit, ROPC...) and you're just looking to propagate the JWT through the distributed architecture for authZ purposes...
In terms of sending custom message headers, I have done this with RabbitMQ and MassTransit, but it was for (OpenTracing) trace Id propagation between asynchronous message broker operations. Repo is on GitHub here - might give you some ideas about how to achieve this...
[edit] following clarification below:
Below are some options I can think of - each one comes with some security implications:
Give the async (windows service) consumer the JWT signing key. If
you go down this path it probably makes more sense to use symmetric
signing of JWTs - any service with the symmetric key would be able
to re-create JWTs. You could probably still achieve the same result
with asymmetric signing by sharing the private key, but (IMO) the
private key should be only be known to the authorization server (when using asymmetric signing).
When the user authenticates, request a refresh token by adding
offline_access to the list of scopes specified on the /token
endpoint. You could then pass the refresh token through to the async
(windows service) consumer, which would be able to use the refresh
token to obtain a new access token if the previous one has expired (or just get a new one each time).
There's probably some security considerations that you'd need to
think about before going down this path.
Increase the timeout
duration of the access tokens so that there's enough time for the
async (windows service) consumer to handle the requests. Not sure if
this is viable for your scenario, but would be the easiest option.
Why can't the client simply send both the access and refresh tokens together for every authorized request? If the access token is expired, it wouldn't require two additional trips to retrieve a new access token and finally making the relevant request.
I realize this operation is amortized, but it would lessen the number of requests for very short access tokens. And under SSL, I don't see how adding the refresh token makes this any more vulnerable. Or does it?
I think the main reason is that the refresh token and the access token are sent to different places. The access token is sent to the resource server and the refresh token is sent to the authorization server. In the general case, there's nothing that the resource server can do with the refresh token.
Some reasons:
The access token provides an abstraction layer, replacing different
authorization constructs (e.g., username and password) with a single
token understood by the resource server. This abstraction enables
issuing access tokens more restrictive than the authorization grant
used to obtain them, as well as removing the resource server's need
to understand a wide range of authentication methods.
https://www.rfc-editor.org/rfc/rfc6749#section-1.4
Having the resource servers understand refresh tokens means more work for them when it can / should be abstracted away (by the authorization server).
...
Because refresh tokens are typically long-lasting credentials used to
request additional access tokens, the refresh token is bound to the
client to which it was issued. If the client type is confidential or
the client was issued client credentials (or assigned other
authentication requirements), the client MUST authenticate with the
authorization server as described in Section 3.2.1.
https://www.rfc-editor.org/rfc/rfc6749#section-6
A refresh request requires client credentials. The resource server shouldn't have to ever see the client's credentials.
Refresh tokens are meant to be long-lasting, while access tokens aren't (or shouldn't be).
Can't the server just "upgrade" the temporary credentials to token credentials and retain the same key and secret?
The client can then start doing authenticated calls right away after the recieving the callback from the server stating that the temporary credentials has been "upgraded".
Of cause if the temporary credentials have not be upgrade (i.e. client doesn't wait for callback) the authenticated call fails.
So the question is why make an extra call to the server after the callback to "exchange" temporary credentials for token credentials?
You could implement OAuth in that way, but as I understand it, separating Request Tokens from Access Tokens does provide an extra layer of security.
From the Beginner's Guide:
OAuth includes two kind of Tokens:
Request Token and Access Token. Each
Token has a very specific role in the
OAuth delegation workflow. While
mostly an artifact of how the OAuth
specification evolved, the two-Token
design offers some usability and
security features which made it
worthwhile to stay in the
specification. OAuth operates on two
channels: a front-channel which is
used to engage the User and request
authorization, and a back-channel used
by the Consumer to directly interact
with the Service Provider. By limiting
the Access Token to the back-channel,
the Token itself remains concealed
from the User. This allows the Access
Token to carry special meanings and to
have a larger size than the
front-channel Request Token which is
exposed to the User when requesting
authorization, and in some cases needs
to be manually entered (mobile device
or set-top box).
So, as I understand it, by limiting the Access Token to a channel directly between the consumer (your service) and the provider (the service you're gaining access to), you can obtain a secure Access Token (that is, one the attacker doesn't have) even if the user's machine or the user's network connection to your service is compromised. If the Request Token were simply upgraded, then anyone sniffing the user's network connection could easily obtain the Request/Access Token, which we'd prefer to keep secret since it can be used (with your consumer token, of course), potentially for a very long time, to access the user's data. A server-to-server connection is often more secure.
Also, as is pointed out above, this lets you have a much longer key in cases where the Request Token actually has to be typed out by the user (and so is probably very short).