keycloak + Kong API Gateway - token

We are using keycloak to handle authentication (client/secret) in our API Gateway.
The Kong api service uses konnect-managed-plugin to refer to keycloak to authenicate client credentials and return a bearer token.
Future calls to other endpoints use oauth2-introspection to verify the bearer token via keycloak introspection
I almost have this working however, when I authenticate via Kong api gateway, it returns a bearer token, but this token fails introspection.
If I auth straight to keycloak, the bearer token works for introspection.
eg
Token from:
http://kongapigateway.domain/getOAuthToken
NOTE: We have not yet set up ssl on the kong api gateway
Returns:
{"access_token":"ey..ZiUQyw","expires_in":900,"refresh_expires_in":0,"token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
Calling api endpoint in kong that uses introspection fails:
http://kongapigateway.domain/bookings
{
"error_description": "The access token is invalid or has expired",
"error": "invalid_token"
}
Direct call to keycloak introspection also fails:
https://keycloak.domain/auth/realms/{Realms}/protocol/openid-connect/token/introspect
{
"active": false
}
However if I get token direct from keycloak server:
https://keycloak.domain/auth/realms/{Realms}/protocol/openid-connect/token
{"access_token":"eyJhb...4lT8w","expires_in":900,"refresh_expires_in":0,"token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
The token works for both endpoints:
http://kongapigateway.domain/bookings and https://keycloak.domain/auth/realms/{Realms}/protocol/openid-connect/token/introspect
So why doesn't the introspection work for the token returned in the first scenario

You can fix this by setting Keycloak's frontend URL to your public URL. For details please refer to my answer to another but similar question.

Related

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

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
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
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
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.
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 -
org.springframework.security:spring-security-oauth2-jose:5.4.2
org.springframework.cloud:spring-cloud-starter-oauth2:2.1.3.RELEASE
And the class which is giving error is: OAuth2AuthenticationProcessingFilter.java (API Doc)
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.

Is this possible to use cognito's Authorization code grant type as a authorizer in the api-gateway?

I would like to know is there any way or possibility to implement authorization code grant type as Authroizer in securing API gateway? As is searched, 'Authorization code' grant type is most recommended for securing API.
i found below article which explain's using cognito 'client credentials' as grant type to secure the api gateway,
https://medium.com/#awskarthik82/part-1-securing-aws-api-gateway-using-aws-cognito-oauth2-scopes-410e7fb4a4c0
I tried the similar way to create an API gateway, where i have done following integration:
Created user pool 'UI Hosted' in cognito with the grant type - 'Authorization code'
Add the resource server
Choosed default scopes as i don't want to add any new scope
Associated call back uri
Now iam able to access the login page to sign up and sign in and it return the 'Authorization code' in the call back uri
In API gateway
I created an API and integrated some mock response
Attached above user pool as Authorizer in the api gateway and deployed
Now when i invoke the api without passing token, it returns 'Unauthorized'
So i used below approach to extract the access token from cognito
How programtically exchange the authorization code to get the access token from cognito using python
And passed the token in the api header using post man, but still i am getting 'Unauthorized' response
So would like to know what needs to be done in api gateway in order to verify the token or what went wrong in this approach..?
Appreciate if anybody can help on this?
Thanks
Your API's role is to just deal with incoming access tokens from API clients. The API doesn't care what flow was used to get the token. Here is by far the most common behaviour:
UI logs the user in using Authorization Code flow - usually the PKCE variant
This involves the UI calling the Authorization Server - eg AWS Cognito
Once login completes the UI calls the API Gateway URL with an access token / JWT
The API then needs to validate the access token by verifying its signature
Here is some sample code in case it helps:
Lambda Authorizer Code to Verify Cognito Tokens
Web UI Code to implement Authorization Code Flow
AWS API Gateway has built in support for Cognito authorizers as in the screenshot on the left below.
For more control over behaviour you can instead create a custom lambda authorizer in code, which returns an AWS policy document, as in the screenshot on the right. My blog post and the above source code link has some further details, though it is quite detailed / advanced.
finally i got the answer here..
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-user-pool-oauth-2-0-grants/
so i created a simple flask logic here to exchange the auth code to get the 'id_token' from cognito, which further can pass in the api header to get the response.
def getToken(auth_code):
response=''
try:
print("Code is", auth_code)
response = requests.post(url + '/oauth2/token',{'Content-Type':'application/x-www-form-urlencoded', 'grant_type': grant_type, 'client_id': App_client_id, 'code': auth_code, 'redirect_uri': 'http://localhost:5000/login'})
if response.status_code != 200:
return "Not a valid response"
print("Response is", response.json())
token_value = response.json()
print("Token value", token_value['id_token'])
return token_value['id_token']
except TypeError as e:
print("Error is",e)

Swagger UI sending "BearerToken" in header, need "Bearer"

I'm making a swagger-UI using swagger 2.0 generated in the integrated Apigee portal.
I'm trying to get the oauth2 client credentials(in swagger 2.0 called application) flow to work in the "try it out" part of the UI.
Note that the input from the user is the clientid and secret, and not the token.
When I try to authorize a get operation and send requests, I can see in the curl representation of the UI that the Authorization header is expressed "Authorization: BearerToken {token}". The token is replaced in the curl string as expected.
Apigee does not support the "BearerToken" prefix, only "Bearer".
Is there a way to force swaggerUI to use the prefix "Bearer" instead of "BearerToken"?
My securitydefinition:
securityDefinitions:
OAuth2:
type: oauth2
flow: application
tokenUrl: 'https://{org-environment}/token'
scopes:
read: Grants read access
My security definition in the path:
paths:
/resources:
get:
security:
- OAuth2: [read]
The name of the token is set from the response the oauth access token generation proxy.
I changed the token.type from "BearerToken" to "Bearer" and this solved my problem.

Invalid Access Token in FitBit

i'm trying to integrate the Fitbit SDK in my iOS app. I have created project on fitbit now i'm running there API in Postman to check profile, but in response it is showing me invalid access token,
{
"errors": [
{
"errorType": "invalid_token",
"message": "Access token invalid: 39ec7defa6f0e33b314bbf6217279b15. Visit https://dev.fitbit.com/docs/oauth2 for more information on the Fitbit Web API authorization process."
}
],
"success": false
}
This is my API https://api.fitbit.com/1/user/-/profile.json and this is what i'm passing in header, Authorization : Bearer Client Secret But it is showing me status code 401 having error of invalid access token . How can i get the access token for my app?
You need to get an OAuth2.0 token from the FitBit authentication service before you can call any other endpoints on the API. You will need to redirect your app to Safari and go to the fitbit authentication service so that the user can log in and authorise your apps access to their FitBit data. The callback will then return an OAuth token that you can pass in subsequent requests.
You will need to call something like this:
https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=%#&scope=%#&redirect_uri=%#"
This is the official fitbit documentation for Authorisation:
https://dev.fitbit.com/build/reference/web-api/oauth2/

WSO2 IS: Error using OAuth Authorization Code flow with SOAP API

I'm using the OAuth Authorization Code flow to authenticate the user and authorize my application against the WSO2 Identity Server. I'm using a simple node/express server, with Passport.js, to get the Access Token, and Postman to use that Access Token to make a few test requests to the SOAP APIs.
When using a Bearer Token method to authorize my application, I get the following error in the IS logs: 0 active authenticators registered in the system. The system should have at least 1 active authenticator service registered. I get the following error in Postman: 500 Internal Server Error, with the following response body, <faultstring>Authentication failure</faultstring>.
Here is what it looks like in Postman:
The same Access Token works with a REST API request, like "https://localhost:9443/scim2/Me".
Can anyone tell me what I'm missing here?
SOAP APIs in WSO2 Identity Server cannot be authenticated with Bearer tokens. They can be authenticated with Basic authentication and cookies. That's the reason for getting Authentication failure in the response.
But REST APIs in the Identity Server can be authenticated with Bearer tokens. So /scim2/Me authenticate successfully with access token.
Try to get the Access token manually from Authorize service and use it
Step 1: Get authorization code
https://<is_server_url>:9443/oauth2/authorize?client_id=<id>&redirect_uri=<callback_url>&response_type=code&scope=openid
You will get an authorization code on the callback URL
Step 2: Call token service to get access token
Post https://<is_server_url>:9443/oauth2/token
Content-Type:application/x-www-form-urlencoded
Authorization:Basic <base64encoded "<client_id>:<client_secret>">
grant_type:authorization_code
scope:openid
code:<code_from_step_1>
redirect_uri:<callback_url>
exp:
client_id=**abcdefgh12345678**
client_secret=**xyzsecretkey**
callback_url=**http://locahost/callback**
scope=openid
server: localhost
base64encode(client_id:client_secret)= base64encode(abcdefgh12345678:xyzsecretkey) => YWJjZGVmZ2gxMjM0NTY3ODp4eXpzZWNyZXRrZXk=
GET https://localhost:9443/oauth2/authorize?client_id=**abcdefgh12345678**&redirect_uri=**http://locahost/callback**&response_type=code&scope=openid
it will make a request back to the callback url with a parameter code, lets say code=this01is02your03code, please check your browser address bar
POST https://localhost:9443/oauth2/token
HEADERS
Content-Type:application/x-www-form-urlencoded
Authorization:Basic **YWJjZGVmZ2gxMjM0NTY3ODp4eXpzZWNyZXRrZXk=**
BODY
grant_type:authorization_code
scope:openid
code:this01is02your03code
redirect_uri:http://locahost/callback
this will return an access token, let say token returned by the server is 12345678ASDFGH
Now you could use this token to call any RestFull or SOAP service
Authorization: Bearer 12345678ASDFGH

Resources