Why does my code skip the try statement and go directly to the catch? - jjwt

I am trying to decode a JWT token string entered by a user and verifying the signature, I am using the io.jsonwebtoken library. I got the "key" by using the "openssl rand -base64 32" command in my terminal. I am currently using "http://jwtbuilder.jamiekurtz.com" to compute the header and payload. I then enter my "key" in the Key field in the jwtbuilder website as shown in the picture in the link below:
jwtbuilder.com with desired header, payload and signature
This is the output when I run the code:
Output of code
package com.okta.developer;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.util.Base64;
import java.util.Scanner;
public class JWTexercise
{
public static void main(String [] args)
{
Scanner input = new Scanner(System.in);
byte[] key = Base64.getDecoder().decode("b8SwFJZVgo+S5Cuhf5LWUeXpHxDm5mp30GCuQHX2TpY=");
System.out.println("Enter your JWT token: ");
String jwtString = input.nextLine();
Jws<Claims> jws;
try
{
// we can safely trust the JWT
jws = Jwts.parser() // (1)
.setSigningKey(Keys.hmacShaKeyFor(key)) // (2)
.parseClaimsJws(jwtString); // (3)
System.out.println("The decoded JWT token id listed below:");
System.out.println(jws);
System.out.println();
System.out.println("The signature is verified!");
}
catch (JwtException ex)
{
System.out.println("Cannot trust JWT because the signature is not verified!");
// we *cannot* use the JWT as intended by its creator
}
}
}

My guess is that the key you are using to create the token is NOT the same when validating the token.
openssl rand -base64 32 should create a random key, but it is unlikely those characters are printable. It looks like http://jwtbuilder.jamiekurtz.com/ uses the key directly entered into the text field, and does NOT base 64 decodes it first. I've never used that site, so this is just a guess.
This essentially means one of the keys is:
byte[] key = Base64.getDecoder().decode("b8SwFJZVgo+S5Cuhf5LWUeXpHxDm5mp30GCuQHX2TpY=");
and the other is:
byte[] key = "b8SwFJZVgo+S5Cuhf5LWUeXpHxDm5mp30GCuQHX2TpY=".getBytes()
The first option is a better practice, but I'm guessing when you this website, you would want to use the second option.

Related

JWT Token Google Cloud Run

I am developing an application with JWT authentication on the google cloud platform. Server side I added authentication via Cloud API Gateway to a cloud run backend. Now I am making a client to generate the JWT token and pass it in the call. To do this I am creating an application that must be deployed on CloudRun and I am following this documentation: https://cloud.google.com/api-gateway/docs/authenticate-service-account#making_an_authenticated_request. My problem is that I don't know how to indicate what it requires as saKeyfile. I tried to put only the name of the file that under src / main / resources / filetest.json but once I try to call the method it tells me file not found. I tried to indicate also the full path. Can anyone help me?
PS I'm using Java
EDIT:
here is my code which is the same of documentation
public void makeCall() {
String fullPath="src/main/resources/TEST1-id.json";
String saEmail="testsa#projectID.iam.gserviceaccount.com";
String audience="auth";
int expiryLenght=600;
String token;
try {
token=generateJwt(fullPath,saEmail,audience,expiryLenght);
System.out.println("Token generated: "+token);
URL url = new URL("apigatewayurl");
makeJwtRequest(token, url);
System.out.println("Call performed");
} catch (IOException e) {
e.printStackTrace();
}
}
private static String generateJwt(final String saKeyfile, final String saEmail,
final String audience, final int expiryLength)
throws FileNotFoundException, IOException {
Date now = new Date();
Date expTime = new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiryLength));
// Build the JWT payload
JWTCreator.Builder token = JWT.create()
.withIssuedAt(now)
// Expires after 'expiraryLength' seconds
.withExpiresAt(expTime)
// Must match 'issuer' in the security configuration in your
// swagger spec (e.g. service account email)
.withIssuer(saEmail)
// Must be either your Endpoints service name, or match the value
// specified as the 'x-google-audience' in the OpenAPI document
.withAudience(audience)
// Subject and email should match the service account's email
.withSubject(saEmail)
.withClaim("email", saEmail);
// Sign the JWT with a service account
FileInputStream stream = new FileInputStream(saKeyfile);
ServiceAccountCredentials cred = ServiceAccountCredentials.fromStream(stream);
RSAPrivateKey key = (RSAPrivateKey) cred.getPrivateKey();
Algorithm algorithm = Algorithm.RSA256(null, key);
return token.sign(algorithm);
}
i've tried to use full path like in example and using only /TEST1-id.json
and here there is project structure. Is a springboot application which i will deploy in cloud run
The OP fixed the issue on this way
In the end I put the file in the root and copied it in the docker image and recover it as an environment variable in cloud run

Establish SSO/set cookies with access or id token/token exchange

I'm allowing users logged in an external application to jump into our application with their access token through Keycloak's identity brokering and external to internal token exchange.
Now I'd like to establish an SSO session in an embedded JxBrowser in our application similar to a regular browser login flow, where three cookies are set in the browser: AUTH_SESSION, KEYCLOAK_SESSION(_LEGACY) and KEYCLOAK_IDENTITY(_LEGACY).
KEYCLOAK_IDENTITY contains a token of type Serialized-ID which looks somewhat similar to an ID token.
Is it possible to create the KEYCLOAK_IDENTITY cookie using the exchanged (internal) access and/or ID token and, provided that the other two cookies are correctly created as well, would this establish a valid SSO session?
Basically all I am missing is how I could obtain or create the Serialized-ID type token.
One way to achieve this:
Implement a custom endpoint following this example
Note that the provider works fine for me without registering it in standalone.xml, I'm just adding the JAR to the Keycloak Docker image.
Add a method that validates a given access token, looks up the user, gets the user session and sets the cookies in the response (most error handling omitted for brevity):
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("sso")
public Response sso(#Context final HttpRequest request) {
final HttpHeaders headers = request.getHttpHeaders();
final String authorization = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
final String[] value = authorization.split(" ");
final String accessToken = value[1];
final AccessToken token = Tokens.getAccessToken(accessToken, keycloakSession);
if (token == null) {
throw new ErrorResponseException(Errors.INVALID_TOKEN, "Invalid access token", Status.UNAUTHORIZED);
}
final RealmModel realm = keycloakSession.getContext().getRealm();
final UriInfo uriInfo = keycloakSession.getContext().getUri();
final ClientConnection clientConnection = keycloakSession.getContext().getConnection();
final UserModel user = keycloakSession.users().getUserById(token.getSubject(), realm);
final UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
AuthenticationManager.createLoginCookie(keycloakSession, realm, user, userSession, uriInfo, clientConnection);
return Response.noContent().build();
}
Disclaimer: I am not completely certain this implementation does not imply any security issues, but since Tokens.getAccessToken(accessToken, keycloakSession) does full validation of the access token, setting the cookies should only be possible with a valid access token.
For CORS, add:
#OPTIONS
#Produces(MediaType.APPLICATION_JSON)
#Path("sso")
public Response preflight(#Context final HttpRequest request) {
return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
.auth()
.preflight()
.allowedMethods("GET", "OPTIONS")
.build();
}
and in sso():
return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
.auth()
.allowedMethods("GET")
.allowedOrigins(token)
.build();
What I am uncertain about is why Firefox preflights the GET request, making it necessary to handle that.

How to access user's email address in Cognito Federated Identities?

I'm trying to set up a basic website (serverless on AWS) that would allow visitors to login with Google and/or Facebook. Currently I'm planning to use S3, Cognito with Federated Identities, API Gateway, Lambda (NodeJS), with DynamoDB. The client app will be using Angular.
I have the social login with Google and Facebook working, and currently I am inserting a row in a "users" table when a user logs in the first time that includes the cognitoId, name, profile picture URL, etc.
I also figure it would be a good design to store the user's information with their email address as the key, instead of something like the cognitoId so that the user can login using different Providers and see the same data. So I need to know the authenticated user's email address, but I figure it should come from Cognito and not straight from the user (since the client app shouldn't be trusted).
I believe that Cognito is storing the user's email address because I have enabled that field as required int the User Pool.
The issue I'm having is that I cannot find any information about how to get the user's email address from Cognito.
The closest that I've come is this post, but I can't find the access token anywhere: How to get user attributes (username, email, etc.) using cognito identity id
This post indicates that I may be able to use GetUser, but I again don't know where the AccessToken comes from: creating user using AWS cognito identity
If I do need to use GetUser and the AccessToken, where does it come from, and how do I generate it? Does it come from the client, or can I get it in Lambda using AWS.config.credentials?
I've been trying to figure this out for a while now and I'm feeling like I'm missing something really simple!
Firstly, go into Cognito Identity provider (in the Cognito console) and make sure your provider "Authorize Scope" is suitable. For example if you clicked on the Google provider your Authorize scope might be "profile email openid". The scope will vary by provider, but whatever scope you are using, it must provide access to the users email.
When your user logs in with an external identity provider (lets say Facebook), Cognito negotiates with Facebook and then calls your Callback URL, which is set in the 'App Client Settings' part of the Cognito console. That Callback contains a parameter called 'code' - the parameter is set in the URL of the Callback made my Cognito. The code is an OAuth token.
Now you have an OAuth token in your client you need to POST that to the AWS Token Endpoint. The token endpoint returns three new tokens in the response; a JWT ID Token, a JWT Access Token and a refresh token. Take the "id_token" attribute from the endpoint response. Parse that id_token as a json string, and take the 'email' element. Now you should have the users email address.
Here is my working example in Java. This is a servlet that gets called by the Cognito Callback.
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.nimbusds.jwt.SignedJWT;
import net.minidev.json.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
public class CognitoLandingServlet extends HttpServlet {
static final Logger LOG = LoggerFactory.getLogger(CognitoLandingServlet.class);
private static final long serialVersionUID = 1L;
public CognitoLandingServlet() {
super();
}
#Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
// Get the OpenID Connect (OAuth2) token passed back from the hosted Cognito
// Login Page
final String code = request.getParameter("code");
LOG.debug(String.format("Cognito OAuth2 code received from Cognito: %s.", code));
if (code != null) {
// do nothing, we have a code as expected
} else {
LOG.debug(String.format(
"Landing page requested without a Cognito code, the request probably didn't come from Cognito"));
// we dont have a token so redirect the user to the application sign in
// page
request.getRequestDispatcher("/signin").forward(request, response);
}
// Exchange the OIDC token for Cognito Access and ID JWT tokens using AWS
// Token
// Endpoint
// There does not appear to be a Java SDK to handle this :(
final String cognitoClientId = System.getProperty("CognitoClientId");
final String redirectUri = System.getProperty("CognitoCallBackUrl");
final String awsTokenEndpoint = System.getProperty("AwsTokenEndpoint");
final String jwt = swapOauthForJWT(cognitoClientId, code, redirectUri, awsTokenEndpoint);
// Complete the login using the JWT token string
loginWithJWT(jwt, request, response);
}
#Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
}
private void loginWithJWT(final String jwtString, final HttpServletRequest request,
final HttpServletResponse response) {
final JSONParser parser = new JSONParser();
SignedJWT signedIdJWT;
try {
// Take the id token
final JSONObject json = (JSONObject) parser.parse(jwtString);
final String idToken = (String) json.get("id_token");
// Access token is not currently used
// String accessToken = (String) json.get("access_token");
// Process the id token
signedIdJWT = SignedJWT.parse(idToken);
final String userId = signedIdJWT.getJWTClaimsSet().getSubject();
// Start NEW Session and start adding attributes
final HttpSession session = request.getSession(true);
session.setAttribute("userId", userId);
final String cognitoUsername = (String) signedIdJWT.getJWTClaimsSet()
.getClaim("cognito:username");
if (cognitoUsername != null) {
user.setUserName(cognitoUsername);
session.setAttribute("username", cognitoUsername);
}
final String email = (String) signedIdJWT.getJWTClaimsSet().getClaim("email");
if (email != null) {
user.setEmail(email);
session.setAttribute("email", email);
}
// Save the user to a database (code removed for stack overflow)
//request.getRequestDispatcher("/dashboard").forward(request, response);
response.sendRedirect("/dashboard");
LOG.info(
String.format("A user with userid %s and email %s successfully signed in", userId, email));
} catch (final java.text.ParseException e) {
LOG.error(
String.format("The JWT token could not be parsed by JOSE library. %s", e.getMessage()));
} catch (final ParseException e) {
LOG.error(String.format("The JWT token could not be parsed by JSON simple library. %s",
e.getMessage()));
} catch (final IOException e) {
LOG.error(String.format("Failed to request webpage at the end of the login process - io. %s",
e.getMessage()));
}
}
private String swapOauthForJWT(final String cognitoClientId, final String oauthCode,
final String redirectUri, final String awsTokenEndpoint) throws IOException {
// Build the URL to post to the AWS Token Endpoint
final String urlParameters = String.format(
"Content-Type=application/x-www-form-urlencoded&grant_type=authorization_code&client_id=%s&code=%s&redirect_uri=%s",
cognitoClientId, oauthCode, redirectUri);
LOG.debug(String.format("User is swapping OAuth token for a JWT using URL %s", urlParameters));
final URL url = new URL(awsTokenEndpoint);
final URLConnection conn = url.openConnection();
conn.setDoOutput(true);
final OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
writer.write(urlParameters);
writer.flush();
// Read the data returned from the AWS Token Endpoint
final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = reader.readLine()) != null) {
responseStrBuilder.append(inputStr);
}
// Close the connection
writer.close();
reader.close();
LOG.debug(String.format("Finished swapping OAuth token for a JWT"));
return responseStrBuilder.toString();
}
}
You also need to add Attribute mappings in your user pool. Check if you have forgotten to add the mappings. You can find "attribute mappings" tab under "federation" inside your User Pool settings
To get the email, you have to request it to the identity provider (facebook, google, user pool).
To get the email from the user pool you have to do something like:
cognitoUser.getUserAttributes(function(err, result) {
if (err) {
alert(err);
return;
}
for (i = 0; i < result.length; i++) {
console.log('attribute ' + result[i].getName() + ' has value ' + result[i].getValue());
}
});
Cognito Identity doesn't save the emails.

Retrieving scores with Application Token

So I have an app set up, and I'm trying to send scores via a server rather than from the application. This allows me to keep scores longer term, whilst also having the social advantages of Facebook.
Now, the problem I have comes in retrieving the scores using the Application Token. I can post absolutely fine using either the Application Token or a User Token, but when retrieving the scores with the Application Token I receive the following:
{
"data": [
]
}
If it was flat out not working or a permissions issue I'd expect to receive an error returned, but the fact it returns an empty array is puzzling. More puzzling is that using a User Access Token retrieves the score absolutely fine, so it's clearly arriving correctly into the Facebook backend.
Is this just a problem with using an App Access Token in this situation? The documentation says that I should be able to use one, but maybe it's mistaken?
I'd also like to clarify that I've run this both in code and via the Graph Explorer, always with no success.
Make sure that you have granted user_games_activity and friends_games_activity permissions
on developers.facebook.com/tools/explorer
from above link you will get an application access_token and add it in your code like this
public void sendDataToFacebookGraphServer()
{
// TODO Auto-generated method stub
final Session session = Session.getActiveSession();
List<String> permissions = session.getPermissions();
if (!isSubsetOf(PERMISSIONS, permissions)) {
Session.NewPermissionsRequest newPermissionsRequest = new Session
.NewPermissionsRequest(UnityPlayer.currentActivity, PERMISSIONS);
session.requestNewPublishPermissions(newPermissionsRequest);
return;
}
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://graph.facebook.com/user_id/scores");
List<NameValuePair> pairs = new ArrayList<NameValuePair>();
pairs.add(new BasicNameValuePair("score", "3000"));
// add this line and try
pairs.add(new BasicNameValuePair("access_token", "add_app_access_token_here"));
try{
post.setEntity(new UrlEncodedFormEntity(pairs));
}
catch(UnsupportedEncodingException e)
{
}
try{
response = client.execute(post);
Log.i("*********Response*******************************************************", response.toString());
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(UnityPlayer.currentActivity,""+response.toString(),Toast.LENGTH_LONG).show();
}
});
}
catch (IOException e1)
{
}
}
Is this supposed to work with the app access token? I don't think it is.
According to the Scores Documentation you can
Retrieve a user's score with the user or app access token (/USER_ID/scores)
Retrieve a user's friends' scores for your app with the user access token (/APP_ID/scores)
Retrieve a user's friends' scores in any app with the user access token (/USER_ID/scores) - though this one respects those users' privacy settings so you won't get an answer for users whose game/app activity is private

Remember me login - Asp script

hello
i have to create a "remeber me login" asp script, have read many scripts about this procedure and have see that many people use to store username and password inside a cookie.
In my opinion it is not secure (safety), some advice
You don't need to store the password. You only need the username and if you make sure that it is properly encrypted it is OK to save it in a cookie. It is important for the user to not be able tamper with the value and if he does the server should detect it. SHA1 for HMAC generation and AES for encryption are commonly used algorithms.
It's best practice NOT to store usernames and passwords in client side cookies. Store some kind of opaque reference instead that matches something in your server side authentication database.
Even better still encrypt this value before storing it in a cookie.
public partial class Login : System.Web.UI.Page
{
StudentService.Service1Client studentCleint = new StudentService.Service1Client();
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnLogin_Click(object sender, EventArgs e)
{
lbnLogin.Text = studentCleint.login(txtusername.Text, txtPassword.Text);
if (lbnLogin.Text == "Succesful")
{
Response.Redirect("Students.aspx");
}
else
{
lbnLogin.Text = "failed";
}
}
}