I got a warning message like this "JWT token does not begin with Bearer String" before I generate the token, and it's also more warning like this when I open swagger.
your have to provide the Bearer String,
must of the libraries out there provide and automatic way of doing that,
for example with io.jsonwebtoken
long now = (new Date()).getTime();
String token = Jwts.builder()
.setSubject("username")
.claim("roles", "ROLE_ADMIN, ROLE_USER")
.signWith(key, SignatureAlgorithm.HS512)
.setExpiration(new Date(now + 86400))
.compact();
here token starts with Bearer
Related
I am trying to validate my json token but i am not able to do that,
Here is my sample token
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payloads:
{
"admin": false,
"School_ID": 123,
"name": "XXXXXX",
"sub": "XXXXXXXX"
}
Singature:
Key
My problem is as soon as i am trying to manipulate JSON web token and change the value of admin 'false' to 'true', it is bypassing my API and becoming as an admin user from the normal user, to prevent that i tried using
token, err: = new(jwt.Parser).ParseWithClaims(tokenString, newClaims(), func( * jwt.Token)(interface {}, error) {
return tokenString, nil
})
but problem still there can anyone help me how to fix that issue as its critical security bug and i need to fix it.
First thing, JWT prevents the users from changing the payload because the users couldn't have key to regenerate the JWT token. If you change admin from false to true in the payload, do you regenerate the signature?
For example, you could paste the following text in jtw.io
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c. You'll see valid signature verified.
But, if you change only payload, you'll get invalid signature, like this, eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRHd3d29lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c. You also could copy it to try in jtw.io.
So when you change payload without regenerating the JWT token, you'll get invalid JWT token. When your JWT token is modified (admin: false to true) by users who don't know your key, the users basically could not get the admin permission.
Last, signature in JWT is not the key, it's just a signature to approve this JWT token is signed by your key.
It doesn't look like you're verifying the signature anywhere. You're parsing the token payload, but you don't verify the signature. When you're reading a JWT you have to verify the signature in order to check whether someone has changed the contents of the token. So to prevent exactly what you have done in your example. When you change admin claim to true then the signature will no longer match the payload and you will be able to reject such a token.
I did request:
{
"grant_type": "client_credentials",
"client_id": 8,
"client_secret": "XXNKXXqJjfzG8XXSvXX1Q4pxxnkXmp8tT8TXXKXX",
"redirect_uri": "",
"scope": "*"
}
Then received response below:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGcXXXJSUzI1NiIsImp0aSI6ImEzYjJhZmU5OTYzMTE5MDAyZDAwZmEzNzU0ZGY3ZTRkMTgwYzhlYWRmNGQyOGU5MTI4YjAyYjJmYWQxZjY1NjUzMzAyZjNlZTI4MTgxMDFhIn0.eyJhdWQiOiI4IiwianRpIjoiYTNiMmFmZTk5NjMxMTkwMDJkMXXXYTM3XXXkZjdlNGQxODBjOGVhZGY0ZDI4ZTkxMjhiMDJiMmZhZDFmNjU2NTMzMDJmM2VlMjgxODEwMWEiLCJpYXQiOjE1NDAwMTQxNTUsIm5iZiI6MTU0MDAxNDE1NSwiZXhwIjoxNTcxNTUwMTU1LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.sNSYywfBf27yAojqZclpjliysbQARlYFktzanTMecXXXIai5DgJY0sKhGpHktP5cqirYdemoFKy2nOxzZ8g29gCQQ63zmxe3vpbDz1GAdrjCDWoUlwSXXXHx4VIsdSIzVdi9XyvPKaLKMdoL6nFeWgpgXKGIvHKdiHjKgQbY_08Qa6JMN5Up27qmIOQoXJNAf1nuXvBMabUU_Js7VNspwPfdC8nMZ5zhK1A_c32_lDRtHqkhDfqqBXdUB-inx-zixhn2ODC4b4tkdj7XXXXlVKFxHxKM3aVOMFlmKhypSDwIUB0dPsN8iHcLzkl1yjzRQcOvQEj5BXWLkLCPdkiX2YJuFiWGUm_nxiYoIRV3ptJDeBI5OJI870JTOwBfJePrHTbXmhbjNSQSflLtiOV34wbPQZWH3KMKcsGVYvXXX3rcO5cbZWeeJLGPPYYO-_AWDmdAm-Qsb6Tw1sPxEZRw0dw3zBHnLVrEK9GXXXN2U5wE9Ka3id8ecOJSXSD39X1PyZUB9dJTidmbiWYWgskSTsqLuWfzXXXtlXkb1iOO37kT_Y5zr71Wp1RJ1Fp38yIyHI6fR9hKqeNALSqhv2ALmcSMQsFGTtPG98lGulu-vRJJhgMJ3C3fSTljN7o9BM7Jz-h0ymxC8sSMSNsXakK1qu40vD40zRJMB09sBPjIAVo"
}
which i suppose to use it in authorization header to consume my API, as follow:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGcXXXJSUzI1NiIsImp0aSI6ImEzYjJhZmU5OTYzMTE5MDAyZDAwZmEzNzU0ZGY3ZTRkMTgwYzhlYWRmNGQyOGU5MTI4YjAyYjJmYWQxZjY1NjUzMzAyZjNlZTI4MTgxMDFhIn0.eyJhdWQiOiI4IiwianRpIjoiYTNiMmFmZTk5NjMxMTkwMDJkMXXXYTM3XXXkZjdlNGQxODBjOGVhZGY0ZDI4ZTkxMjhiMDJiMmZhZDFmNjU2NTMzMDJmM2VlMjgxODEwMWEiLCJpYXQiOjE1NDAwMTQxNTUsIm5iZiI6MTU0MDAxNDE1NSwiZXhwIjoxNTcxNTUwMTU1LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.sNSYywfBf27yAojqZclpjliysbQARlYFktzanTMecXXXIai5DgJY0sKhGpHktP5cqirYdemoFKy2nOxzZ8g29gCQQ63zmxe3vpbDz1GAdrjCDWoUlwSXXXHx4VIsdSIzVdi9XyvPKaLKMdoL6nFeWgpgXKGIvHKdiHjKgQbY_08Qa6JMN5Up27qmIOQoXJNAf1nuXvBMabUU_Js7VNspwPfdC8nMZ5zhK1A_c32_lDRtHqkhDfqqBXdUB-inx-zixhn2ODC4b4tkdj7XXXXlVKFxHxKM3aVOMFlmKhypSDwIUB0dPsN8iHcLzkl1yjzRQcOvQEj5BXWLkLCPdkiX2YJuFiWGUm_nxiYoIRV3ptJDeBI5OJI870JTOwBfJePrHTbXmhbjNSQSflLtiOV34wbPQZWH3KMKcsGVYvXXX3rcO5cbZWeeJLGPPYYO-_AWDmdAm-Qsb6Tw1sPxEZRw0dw3zBHnLVrEK9GXXXN2U5wE9Ka3id8ecOJSXSD39X1PyZUB9dJTidmbiWYWgskSTsqLuWfzXXXtlXkb1iOO37kT_Y5zr71Wp1RJ1Fp38yIyHI6fR9hKqeNALSqhv2ALmcSMQsFGTtPG98lGulu-vRJJhgMJ3C3fSTljN7o9BM7Jz-h0ymxC8sSMSNsXakK1qu40vD40zRJMB09sBPjIAVo
Accept:application/json
Content-Type:application/json
I'm able to catch the bearer token with: $token = $request->bearerToken(); in my controller
But I have difficulties to validate it, anyone know how to validate that token?
what i'm trying to achieve is quite simple, if the Bearer token valid then continue... if not then throw 403.
**I'm not using user authentication (no user) so I can't put middleware Auth::api in route (unless you tell me this is possible to validate token only without user).*
Try this and let me know if any error occurs.
Add the following in App/Http/Kernel.php file in protected $routeMiddleware array:
'client.credentials' => \Laravel\Passport\Http\Middleware\CheckClientCredentials::class,
Now in your controller add the following in the beginning:
public function __construct()
{
$this->middleware('client.credentials')->only(['']);
}
add your function names in the array to check if the request has an Authorization header with a token for the function.You do not need to do anything else.
You can check after, using postman with an incorrect token an error will return from the api as the result.
Also as a tip , do not put your "client_secret" on client side , it should not get in the wrong hands.
Use the following to generate a token for the user:
$user = User::where('email',$email)->where('password',$password)->firstOrFail();
$token = $user->createToken('Token')->accessToken;
I am using Keycloak with Spring-Boot in my application. My browser client request keycloak to generate JWT and then sends this JWT to my ZUUL server which validates JWT using keycloak-spring adapter and then i have written a pre-filter to decodes JWT payload and extract username.
I am using com.auth0.java-jwt library to decode JWT like in below snippet
DecodedJWT dJWT=JWT.decode(header);
String username=dJWT.getClaim("preferred_username").asString();
I was wondering if there is anyway i can do this without using external library. I want to use keycloak library to decode JWT explicitly. How can i achieve this?
You have to include keycloak's core library into your dependencies.
Gradle: compileOnly 'org.keycloak:keycloak-core:3.4.2.Final'
Then use the org.keycloak.TokenVerifier to parse the token.
Example:
try
{
// deprecated: AccessToken token = RSATokenVerifier.create(tokenString).getToken();
AccessToken token = TokenVerifier.create(tokenString, AccessToken.class).getToken();
System.out.printf("iss = %s%n", token.getIssuer());
System.out.printf("sub = %s%n", token.getSubject());
System.out.printf("typ = %s%n", token.getType());
}
catch (VerificationException e)
{
// some error handling
}
You can also activate various verifications on the RSATokenVerifier and in particular the signature validation by setting the public key:
RSATokenVerifier.create(tokenString).checkActive(true).publicKey(key).verify().getToken()
As i am using keycloak to authenticate jwt, it decodes jwt and puts details into SecurityContextHolder and I just pulled the details from there it self. here is the code.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
if (authentication.getPrincipal() instanceof KeycloakPrincipal) {
KeycloakPrincipal<KeycloakSecurityContext> kp = (KeycloakPrincipal<KeycloakSecurityContext>) authentication.getPrincipal();
// retrieving username here
String username = kp.getKeycloakSecurityContext().getToken().getPreferredUsername();
}
}
this solved it for me.
I Successfully Implemented the authorization step and i got the code value with redirect_uri. and while Implementing FitBit OAuth AccessToken Request (https://wiki.fitbit.com/display/API/OAuth+2.0)
i am getting the following error:
WARN : org.apache.http.impl.client.DefaultHttpClient - Authentication error: Unable to respond to any of these challenges: {oauth=WWW-Authenticate: OAuth realm="https%3A%2F%2Fapi008-g4.prod.dal05.fitbit.com"}
{"errors":[{"errorType":"oauth","fieldName":"n/a","message":"invalid_request, Missing grant_type parameter value"}],"success":false}
as per document i supplied every recommended values and the code is like:
String authString = fitbit.getClient_id()+":"+fitbit.getClient_secret();
String authEncString = Base64.getEncoder().encodeToString(authString.getBytes());
url = fitbit.getAccesstoken_uri() + "?code="+code+"&grand_type="+fitbit.getGrant_type()+"&client_id="+fitbit.getClient_id()+
"&redirect_uri="+fitbit.getRedirect_url();
String url3 = fitbit.getAccesstoken_uri();
HttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(url3);
request.addHeader("Authorization", "Basic " + authEncString);
request.addHeader("Content-Type","application/x-www-form-urlencoded");
request.addHeader("code", code);
request.addHeader("grant_type",fitbit.getGrant_type());
request.addHeader("client_id", fitbit.getClient_id());
request.addHeader("redirect_uri", fitbit.getRedirect_url());
HttpResponse response = httpClient.execute(request);
String json = EntityUtils.toString(response.getEntity(), "UTF-8");
Here grant_type value is authorization_code
Accesstoken_uri : https://api.fitbit.com/oauth2/token
Can any one solve this?
Add grant_type=authorization_code as a body parameter instead of header parameter.
You can add client id and redirect_uri also as a body parameter.
I am trying to exchange an OAuth one-time use code that I got from my client-side app into a access token and refresh token on my server. The response that I get is:
{
"error" : "redirect_uri_mismatch"
}
My POST request is:
POST /o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
code={My Code}&
client_id={My Client ID}&
client_secret={My Client Secret}&
grant_type=authorization_code
I have checked my Client ID and Client Secret against those in the API Console and they match.
I get the one-time use code on my client with the following Java code:
static final List<String> SCOPES = Arrays.asList(new String[]{"https://www.googleapis.com/auth/plus.login","https://www.googleapis.com/auth/userinfo.email"});
String scope = String.format("oauth2:server:client_id:%s:api_scope:%s", SERVER_CLIENT_ID, TextUtils.join(" ", SCOPES));
final String token = GoogleAuthUtil.getToken(c, email, scope);
I have a redirect_uri in my API Console, but since I am trying to use cross-client authorization (as described here), I deliberately left it out of the POST request as is required:
When it exchanges the code for tokens, it should not include the “redirect_uri” argument in the POST.
Any idea on what I am doing wrong?
It turns out that "should not include the 'redirect_uri' argument in the POST" does not mean to completely omit the redirect_uri field. It instead means that the redirect_uri field should have an empty value.
My new, working POST is:
POST /o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
code={My Code}&
client_id={My Client ID}&
client_secret={My Client Secret}&
redirect_uri=''&
grant_type=authorization_code
In my case the following - https://stackoverflow.com/a/25184995/775359 - answer was helpful:
var post_data = {
code : code,
client_id : google_client_id,
client_secret : google_client_secret,
redirect_uri : 'postmessage',
grant_type : grant_type,
};
Setting redirect_uri to postmessage solved my issue with redirect_uri mismatch.
EDIT: Another answer referencing postmessage: https://stackoverflow.com/a/18990268/775359