We are using Spring Cloud Netflix Zull as Gateway Application for our backend services. Front end is Angular. But we are testing endpoints in Postman only(Front end is under development). We have one module called LoginServiceModule and another is ZullServerModule. The LoginSericeModuke take username and password from front end and make call to the following oauth/token endpoint by including required headers and body.
http://localhost:XXXX/login
and response is
{
"access_token": "XXXXXXXXX",
"token_type": "bearer",
"refresh_token": "XXXXXXXXX",
"expires_in": 3600,
"scope": "read write",
"jti": "XXXXXXXXXXX"
}
and the ZullServerModule contains ZullServerConfiguration, Authorisation Server Configuration and Resource Server Configuration and etc...
The LoginModule internally calls oauth/token end point like this.
ResponseEntity<String> loginResponse = restTemplate.exchange("http://localhost:XXXX/oauth/token", HttpMethod.POST, entity, String.class);
and the response is ..
{
"access_token": "XXXXXXXXX",
"token_type": "bearer",
"refresh_token": "XXXXXXXXX",
"expires_in": 3600,
"scope": "read write",
"jti": "XXXXXXXXXXX"
}
we extract the access_token from the response and call following endpoint...
http://localhost:XXXX/ProjectName/api/endpointname?access_token={access_token}.
But when the access_token expires, and when i access the above backend service url, its saying
{
"error": "invalid_token",
"error_description": "Access token expired:XXXXXXXXXXXXXXX(access_token)"
}
I know its expired and tried regenerating access_token with help of refresh_token in terminal like this
curl clientID:clientSecret#localhost:XXXX/oauth/token -d grant_type=refresh_token -drefresh_token={refresh_token}
But i need to include this in our code and don't know where to place it.
After searching on net, i came across about ZullFilter. I tried all pre, route and post filters. For each request they are all executed (i.e all long as access_token not expired), but when token expires and if i test endpoint, none of the filters are executed and i am getting error response
{
"error": "invalid_token",
"error_description": "Access token expired: XXXXXXXXXX"
}
I have placed sysouts in run method of every filters. I don't know much about filterOrder also.
#Override
public Object run() throws ZuulException {
System.out.println("pre filter...");
RequestContext context =
RequestContext.getCurrentContext();
HttpServletResponse response = context.getResponse();
System.out.println(response.getStatus());
return null;
}
I want to control access_token generation with help of refresh_token. How could i code that whenever the access_token expires and if i access resources after expire,then i get to know that token expired and re generate access_token and call the previous call with new access_token.
Typically it is the client that is responsible for maintaining its own token and refreshing it when it is about to expire. Moving this logic into your Zuul layer seems like a really bad idea. Think about the implementation for a second, how would it work?
Once a client's token has expired, it would be calling your endpoints with a perpetually expired token that Zuul would have to try and refresh with every request. This would add a lot of overhead for each API call. You could possibly introduce some kind of hack where you always pass back a new token in a response header or something... but at this point you'd be violating the authorization flow of OAuth2.
Related
I have a client app using jax-rs Client and making REST calls to bunch of services. One of those services uses bearer token authorization, and after first hard authentication (BASIC) lends a json with an access token, expiry delay, token type etc...
{
"access_token": "hashed_value",
"token_type": "bearer",
"expires_in": 1800 }
I wish to know what would be the best practices for managing this token for replay. Would it be via a global structure like a Map in memory that I would access at each request and update when token comes to expiration ? an other structure ?
To get a new token after expiration, would it be better to manage it requesting a new token based on a 401 status code ? or by requesting it automatically 1 minute or so before the 1800 seconds passes ?
Due to the architecture of our product, the loadbalancer that I use is not able to manage correctly my servers. To solve this point, I have create a kind of manager which forward the call to the correct server by requesting it directly from the first server when it is needed. The issue is to do that I forward the user keycloak token string in the header of the second call. It works pretty well but after few minutes to token is invalidate. I tried to refresh it by many way without success. I have 2 constraints. I can't reask the credentials to the user, and I can't use the keycloak secret because I'm working in a multi tenant / multi realm app. Do you have any idea?
I'm assuming that you have just the access_token on login.
First you need to exchange that access token for a refresh token (and another access token), you do this by calling:
HTTP POST {{host}}/auth/realms/{{realm}}/protocol/openid-connect/token
HEADERS:
Content-Type: application/x-www-form-urlencoded
BODY:
grant_type: urn:ietf:params:oauth:grant-type:token-exchange
subject_token: the_user_access_token
client_id: your_client_id (in my case this is "public")
requested_token_type: urn:ietf:params:oauth:token-type:refresh_token
you will get back a response that looks like this:
{
"access_token": "access_token_value",
"expires_in": access_token_time_in_seconds,
"refresh_expires_in": refresh_token_time_in_seconds,
"refresh_token": "refresh_token_value",
"token_type": "bearer",
...
}
So with this you have what you need to actually refresh your token with the following request:
HTTP POST {{host}}/auth/realms/{{realm}}/protocol/openid-connect/token
HEADERS:
Content-Type: application/x-www-form-urlencoded
BODY:
grant_type: refresh_token
refresh_token: refresh_token_value
client_id: public
This will get you the following response:
{
"access_token": "access_token_value",
"expires_in": access_token_time_in_seconds,
"refresh_expires_in": refresh_token_time_in_seconds,
"refresh_token": "refresh_token_value",
"token_type": "bearer",
...
}
Now you can repeat step 3 and 4 up until the maximum session duration.
I am following the example for JWT Auth and getting a new token each time. I need to be able to request a new token only when the current one is about to expire. The examples do not show how to access this information and the methods exposed in the api do not seem to apply. Does anyone know how to use the api to get this or am I going to have to track it manually?
If you are using JWT then you will be getting response like below:
{
"access_token": "eyJ0eX...AnHDQ0bbA",
"token_type": "Bearer",
"expires_in": 3600
}
It returns expires_in value in seconds, this will help you in knowing when the token is about to expire, also you should know the expiry time because while creating JWT you would have populated exp claim attribute in JWT and this exp time is your token expiry time.
I have create an OAuth application, it gives me Client ID:xxx Client Secret:yyy
Redirect URIs, Authorize URL, Access Token URL now what should I do? In coding wise, I am working in Java.
Thanks for your interest in working with our API!
I think the best explanation of how to work with our API using would is in the documentation on our developers site.
Take a look at that, and let us know if you have any other questions.
Particularly useful from that page is this sample flow that your app might go through:
# Redirect the user to this page
https://www.coinbase.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_CALLBACK_URL&scope=user+balance
# If the user accepts, they will be redirected to:
YOUR_CALLBACK_URL?code=CODE
# Initiate a POST request to get the access token
https://api.coinbase.com/oauth/token&
grant_type=authorization_code&
code=CODE&
redirect_uri=YOUR_CALLBACK_URL&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
# Response containing the 'access_token'
{
"access_token": "...",
"refresh_token": "...",
"token_type": "bearer",
"expire_in": 7200,
"scope": "universal"
}
# Now you can use the 'access_token' to initiate authenticated requests
https://api.coinbase.com/v1/account/balance?access_token=...
# Response
{
"amount": "50.00000000",
"currency": "BTC"
}
Question
When using the Google Plus Sign In Api with the Play Framework do you have to set headers in a different way? Is there something I am doing wrong here?
Background
I am using Play Framework(in Java) to use the Google Plus Sign in Api.
I am running into issues on the second leg of OAuth authentication, exchanging the Authorization Code for a Token.
Basic OAuth Flow
Pretty Picture
Redirect user to User login/Consent screen
This asks the user if they want to grant you application permission to the requested scopes
URL: https://accounts.google.com/o/oauth2/auth
Exchange Authorization Code for a Token
If the user gives your application permission then they will be redirected to a URL you specify, in that URL(As a GET param) will be an Authorization Code.
Your application can then use this Authoriztion Code to get a Token from the server
Your application does this by making a HTTP request to a endpoint on the Google Servers(Or whatever service you are using)
URL: https://accounts.google.com/o/oauth2/token
Use Token in API requests
The Issue
To Exchange the Authorization Code for a Token, with the Google Plus Sign In Api, you must make a POST request to https://accounts.google.com/o/oauth2/token with the following perimeters
{
"code": "Security Code Returned from Step 1",
"client_id": "Client Id that was given to you in GApi Console",
"client_secret": "Client Secret that was given to you in the GApi Console",
"redirect_uri": "Redirect Uri you specified in the GApi Console",
"grant_type": "authorization_code"
}
However when I make this request with all the correct parameters I get this error
{
"error" : "invalid_request",
"error_description" : "Required parameter is missing: grant_type"
}
From the Google Plus Sign in Api
To make HTTP requests in The Play Framework you use the WS Library. I make the request like this
public static F.Promise<Result> OAuthCallback(String state, String code){
/*
Note:
- The GoogleStrategy class is just a class that holds all my GApi credentials
- The parameters (String state, String code) are just GET params from Step 1, returned by the GApi
*/
//Make URL builder
WSRequestHolder requestHolder = WS.url(GoogleStrategy.getTokenUrl);
//Set headers
requestHolder.setHeader("code", code);
requestHolder.setHeader("client_id", GoogleStrategy.clientId);
requestHolder.setHeader("client_secret", GoogleStrategy.clientSecret);
requestHolder.setHeader("redirect_uri", GoogleStrategy.redirectUri);
requestHolder.setHeader("grant_type", GoogleStrategy.grantType);//GoogleStrategy.grantType = "authorization_code"
//Make HTTP request and tell program what to do once the HTTP request is finished
F.Promise<Result> getTokenPromise = requestHolder.post("").map(
new F.Function<WSResponse, Result>() {
public Result apply(WSResponse response){
return ok(response.asJson());//Returning result for debugging
}
}
);
return getTokenPromise;//Return promise, Play Framework will handle the Asynchronous stuff
}
As you can see, I set the header grant_type. Just to make sure setting headers was working I made a program that spits out the headers of a request in NodeJS(Source) and this was the result
{
"HEADERS": {
"host": "127.0.0.1:3000",
"code": "4/qazYoReIJZAYO9izlTjjJA.gihwUJ6zgoERgtL038sCVnsvSfAJkgI",
"grant_type": "authorization_code",
"client_secret": "XXXX-CENSORED FOR SECURITY PURPOSES-XXX",
"redirect_uri": "http://127.0.0.1:9000/api/users/auth/google/callback",
"client_id": "XXXX-CENSORED FOR SECURITY PURPOSES-XXX",
"content-type": "text/plain; charset=utf-8",
"connection": "keep-alive",
"accept": "*/*",
"user-agent": "NING/1.0",
"content-length": "14"
}
}
I think those are not to be sent as headers but as a body. In the link you provided there is an example:
POST /o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
redirect_uri=https://oauth2-login-demo.appspot.com/code&
grant_type=authorization_code
So pass them to your post call:
StringBuilder sb = new StringBuilder();
sb.append("code=").append(code)
.append("&client_id=").append(GoogleStrategy.clientId)
.append("&client_secret=").append( GoogleStrategy.clientSecret)
.append("&redirect_uri=").append(GoogleStrategy.redirectUri)
.append("&grant_type=").append(GoogleStrategy.grantType)
requestHolder.setContentType("application/x-www-form-urlencoded")
.post(sb.toString());