Spring OAuth2 - JWT token working on server but not on localhost?

Viewed 84

I am seeing that myapp is able to process the OAuth2 JWT token properly on the server, but it is giving token conversion error on localhost.

My Flow is as below -

On Server, the myapp is behind our custom api-gateway

  1. Getting Access Token - Through postman, I hit api-gateway token endpoint which in turn calls the authserver token endpoint. And I get OAuth JWT Token as response.

So, to summarize, postman
request: --(creds)--> api-gateway --(samecreds) --> auth-server
response: jwt token <-- (same jwt)-- api-gateway <--(jwt)-- auth-server

  1. Next, I hit myapp endpoint - Again through postman, I hit api-gateway endpoints, which in turn hits the corresponding myapp endpoints. And I get the required response. For this request, before hitting the api-gateway, I set the header Authorization: Bearer JWT from step1

As the myapp developer, i know that the api-gateway is re-sending the JWT token to myapp using the same header mechanism i.e Authorization: Bearer JWT from step1. From the logs, i see this is the same value that I provided in postman when hitting the api-gateway

So, postman request: --(jwt)--> api-gateway --(same jwt)--> myapp
and response is some data, which is trivial for this discussion.

The decoded JWT Payload is as below:

{
  "app-userId": "c54a-4140-9fa0-0f39",
  "user_name": "abc@xyz.com",
  "scope": [
    "all"
  ],
  "exp": 1656929583,
  "authorities": [
    "app1_viewer",
    "app1_modifier",
    "app2_viewer",
    "app2_blog_creator],
  "client_id": "api-gw-client"
  ...
}

Plz note - "client_id": "api-gw-client" field in the above payload. So the auth-server is issuing the token to api-gateway client.


Now on mylocal dev env i.e myapp running on localhost - i am trying to achieve similar flow as on server.

On localhost, the myapp is NOT running behind api-gateway, but its hit directly

  1. Getting Access Token - Through postman, I hit api-gateway (same server instance as in server flow above. i.e api-gateway/auth-server's are not running on localhost but are running on server) token endpoint which in turn calls the authserver token endpoint. And I get OAuth JWT Token as response.

So, postman request: --(creds)--> api-gateway --(samecreds) --> auth-server
response: jwt token <-- (same jwt)-- api-gateway <--(jwt)-- auth-server

Yes, this token can be used in Server Flow Step 2 and it works. And the decoded JWT token payload is same as I posted earlier i.e

{
  "app-userId": "c54a-4140-9fa0-0f39",
  "user_name": "abc@xyz.com",
  "scope": [
    "all"
  ],
  "exp": 1656929583,
  "authorities": [
    "app1_viewer",
    "app1_modifier",
    "app2_viewer",
    "app2_blog_creator],
  "client_id": "api-gw-client"
  ...
}

Plz note again - "client_id": "api-gw-client" field in the above payload. So the auth-server is issuing the token to api-gateway client. I am not sure yet if this field is trivial or important.

  1. Next, I hit myapp localhost endpoint - Again through postman, But I hit myapp endpoints directly (not through api-gateway running on localhost). And for this request, before hitting, I set the header Authorization: Bearer JWT from step1.

BUT THIS TIME I GET ERROR:

p.a.OAuth2AuthenticationProcessingFilter : Authentication request failed: error="invalid_token", error_description="Cannot convert access token to JSON"

I want to know what is causing this error. I don't think the client_id: api-gw-client is causing this. Anyhow, I created a signed jwt token with client_id: myapp and used it in request. But still I get same error.
The key I am using on localhost matches(corresponds) to the one that the auth-server is using to sign. I double checked. So its for sure not a key issue.

I need the localhost based setup working, so i can test my api's locally with out deploying to server(deploying to server is quite time consuming in my case). So this setup is quite important for me to meet my deadlines. Any answers/suggests are greatly appreciated.


The Spring OAuth2 Libraries used in my project are as below -

  1. org.springframework.security:spring-security-oauth2-jose:5.4.2
  2. org.springframework.cloud:spring-cloud-starter-oauth2:2.1.3.RELEASE

And the class which is giving error is: OAuth2AuthenticationProcessingFilter.java (API Doc)

1 Answers

Sorry, I won't answer your question (@Toerktumlare is right, your security configuration is missing) but will instead try to explain why I would not make the API gateway an OAuth2 client.

Simply put, the role of an API gateway is to be a black box for a system resources, which could make it seen as a resource-server, but it should, in my opinion, remain transparent to OAuth2 (and other authentication mechanisms). Keep things simple:

  • clients (UI) handle user login (when needed)
  • resource-servers control access to resources
  • gateway (if any) should remain as transparent as possible, just offering a single entry-point for all resources.

In your scneario:

  • how do you expect things to happen if you're asked to implement Multi Factor Authentication: in addition to login / password, require one of biometry, external app validation, Goolgle authenticator token or whatever? Do you really intend to implement all that in the gateway in addition to the authorization-server?
  • if it always get api-gw-client as client ID, how can your authorization-server achieve client based processing (like checking requested scopes are legit for a client, or adding client authorities to the access-tokens)?
  • what about multi-tenant scenarios (R1 and R2 resource-servers behind the gateway expect identities from different issuers, or C1 and C2 clients do not authenticate users against the same authorization-server)?
  • what if some clients and resource-servers use something else than OAuth2?

Also, your client(s) must know what media-type is accepted and produced at each API endpoint (XML, JSON, PDF, multipart, etc.) and set Content-type and Accept headers accordingly. Why would it be different for Authorization header?

In my opinion, if you want to save time and energy, use your API gateway to proxy your resource-servers only. Leave authorization-server(s) appart. Same for external APIs you do not maintain but your client(s) need, if any (Tweeter feed, Google API, etc.).

This is how I always configure my resource-servers:

  • Authorization header is missing or invalid (malformed, expired, wrong issuer, bad audience, etc.) => 401 (unauthorized) and not 302 (redirect to login)
    • in multi-tenant scenarios, why should resource-server bother figuring out which authorization-server to redirect to ?
    • What should be the meaning of a redirect for clients which are not running in a browser, like mobile apps?
  • Authorization header is valid but associated claims fail to pass access-control (bad authorities or not the expected subject for instance) => 403 (forbidden)

Clients should know how to aquire required access-token(s) and how to set Authorization header when it sends a request (this is what you curently do with Postman). It can even intercept 401 to trigger user authentication and then retry failed resource access. Serious OAuth2 client-side librairies (like angular-auth-oidc-client for Angular) provide with such features.

With such clients, gateway can act as facade for your resource-servers (and authorization-server or external APIs if you like, but why?), forwarding Authorization header and completely ignoring users login.

Related