Using Cognito Authentication with Elastic Load Balancer - java

I have a ReactJS front end website which uses AWS Cognito for authentication, this is working fine, I can sign in, up, out, etc. My back end is a set of Java web services running in Docker containers in AWS Fargate all behind an Elastic Load Balancer.
I noticed that in the ELB, you can add Authentication to the listeners which will check the HTTP header for the jwt token and authenticate it before forwarding to the relevant micro service. I've come a cross an issue where when I set the 'Authenticate...' rule, it comes back with an error that I need to add a client secret. I can't add a client secret to my Cognito setup because I'm accessing it from ReactJS and apparently Javascript doesn't work with Cognito with Client Secret added.
So if I can't use this method, I need some way of authenticating HTTPS requests when they get to my Java microservices. From my Java service, it feels like I need to somehow access AWS Cognito to check the user session but that feels wrong.
Any ideas how this should work?
Thanks

You can consider to use pure JS to authenticate with AWS Cognito without client secret which is an optional. I did create a App Client without Client Secret and it did work.
Reference: https://docs.aws.amazon.com/cognito/latest/developerguide/authentication.html
// Amazon Cognito creates a session which includes the id, access, and refresh tokens of an authenticated user.
var authenticationData = {
Username : 'username',
Password : 'password',
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId : 'us-east-1_ExaMPle',
ClientId : '1example23456789'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username : 'username',
Pool : userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
var accessToken = result.getAccessToken().getJwtToken();
/* Use the idToken for Logins Map when Federating User Pools with identity pools or when passing through an Authorization Header to an API Gateway Authorizer */
var idToken = result.idToken.jwtToken;
},
onFailure: function(err) {
alert(err);
},
});
Also, as you are using ReactJS, you can try Amplify:
https://aws-amplify.github.io/docs/js/authentication
I understand that while you have so many layers and sometimes you want to configure it as you want, you can try to make yourself a simple authenticating function with AWS Lambda.

Related

Is there a way to read the form data of an incoming request inside a CustomProtocolMapper [Keycloak SPI]

I am trying to implement Keycloak as an IAM, the Problem that I have is, that I need to authenticate the user (already working) but also authorize him. The authorization should be accomplished through keycloak directly, but the security information (like roles, etc.) is available over an REST interface externally.
The way it is working now goes as followed:
authentication request (default)
"authorization" request → keycloak server (with extra form param)
keycloak server → CustomProtocolMapper (calls external REST interface and adds claims to Token)
Token → frontend client
This worked until I used a refresh token to refresh the ID Token. The Cookie that is used to authenticate the user is not sent to the keycloak server, because of security reasons (Cookie labeled as "Secure" but connection over HTTP). To fix this I upgrade my keycloak server to use HTTPS/TLS and now i am getting errors because the "HttpRequest" is no longer available. Any ideas on how to get the Request Body of an HTTPS Request inside a CustomProtocolMapper? I know that the Authenticator Providers has access to it, but i dont know/ didnt find anyway to add claims to the Token inside it.
#Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession,
ClientSessionContext clientContext) {
String contextParamName = mappingModel.getConfig().get(CONTEXT_PARAMETER);
// worked with http
HttpRequest request = keycloakSession.getContext().getContextObject(HttpRequest.class);
String contextId = request.getFormParameters().getFirst("activeContext");
LOGGER.warn("activeContext: " + contextId);
}
Thanks in advance,
best regards

How to check user credentials in AZURE AD when acquired token using appId/appSecret (adal4j)?

I've created webapp (not native) in Azure AD. I have java code (adal4j) that
acquire token using appId/appSecret credentials:
String clientId = "xxxxxxxxxxxxxxxxxxxxxx";
String clientSecret = "yyyyyyyyyyyyyyyyyyyyyy";
String resourceUrl = "https://graph.windows.net";
String authorityUrl = "https://login.microsoftonline.com/zzzzzzzzzzzzzzzz/oauth2/authorize";
ExecutorService executorService = Executors.newFixedThreadPool(4);
Optional<UserInfo> userInfo = Optional.empty();
try {
AuthenticationContext authContext = new AuthenticationContext(authorityUrl, false, executorService);
Future<AuthenticationResult> future = authContext.acquireToken(resourceUrl, new ClientCredential(clientId, clientSecret), null);
AuthenticationResult result = future.get();
}
Now I would like to check if specified user/password combination is in Azure AD and if yes then get First and Last name of this user.
Is it possible to do this usinq acquired token ? How to write such code using adal4j ?
It sounds like what you're really trying to do is sign in a user and get their first/last name. As the comment said, the pattern suggested is not a valid one and would represent a security issue. Additionally, the use for clientId and clientSecret is not exactly for user credentials, but for app credentials. This is used for flows without user interaction for service/api applications, and doesn't sound like what you'll want.
Now, to achieve this you'll be using the OpenID Connect protocol. To simplify what will happen, your app (upon user trying to sign in) will redirect to the Microsoft sign in page (https://login.microsoftonline.com), enter their credentials and fulfill any other authorization requirements, consent to your app, and then redirected back. When they come back, your app will receive an ID Token which can be validated and used to get information about the user that has just sign in. During this time, Azure AD / Microsoft will also set a cookie on the browser so the user will get SSO across their account.
In terms of how to achieve this, I recommend following the ADAL4J Code Sample. This will get your app an ID Token, and also an Access/Refresh token that you can use to call the Microsoft Graph API. This API can also get you information about the user (basic profile info), but also their Office365, Intune, and Windows data.

Deleting a cognito user as admin

String userPoolId="testPoolID";
String username= "testuser";
String amazonAWSAccessKey="test access key";
String amazonAWSSecretKey="test secret key";
AdminDeleteUserRequest req = new AdminDeleteUserRequest();
req.setUsername(username);
req.setUserPoolId(userPoolId);
AWSCredentials credentials = new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey);
AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
credentialsProvider.getCredentials();
req.setRequestCredentialsProvider(credentialsProvider);
AWSCognitoIdentityProvider provider = new AWSCognitoIdentityProviderClient();
provider.adminDeleteUser(req);
This is the code snippet for deleting a user from cognito User pool. How we can delete a user from cogito without providing credentials using java ?
One way to do this would be to put this code in a lambda & expose it via API Gateway. Create another userpool for admins (its free!) & enable cognito userpool authorizers for your API. Now in your code, show a login screen (use Cognito's built-in UI) ,get the ID tokens and use this ID token while calling your API. This way your code will dynamically get tokens & so no hard-coded credentials.
Another way would be to use Cognito Federated Identities but in this case no lambda + API Gateway is needed. Again, create a userpool for Admins & add this as an Auth Provider in a Cognito Identity Pool. Now, login to the Admin userpool, get an Id token, use this token in the login map for the IdentityPool & get temporary credentials using GetCredentialsForIdentity. Just make sure that the Auth role for the Cognito Identity Pool has appropriate permissions to perform userpool actions.
Sorry its late, might be helpful for others who stumble on this.
If your code is in a lambda you can use the following code
AWSCognitoIdentityProviderClientBuilder.standard()
.withCredentials(DefaultAWSCredentialsProviderChain.getInstance())
.withRegion(region).build();
Note: Check if your lambda execution role is having access to cognito

Login with amazon and access aws resources like S3, EC2 etc

I have a web application that enables a user to manage AWS resources easily like start an Ec2 instance using GUI. Different users can use this web application. I have implemented my own user management. It is possible that some users don't want to create an account to my web-app and store their credentials to make requests to AWS resources. It may not be secure for them. So, I want to build a mechanism using following flow.
User login to my web-app using login with amazon
Login with amazon will provide an access token to me
Fetch temporary credentials using this token
Use these credentials for future request to AWs resources
In this way, there will be no permanent credentials stored in my web-app.
I'm using java for my backend services. So is there any AWS APi's to do so?
I have tried the following code:
val client = new AmazonCognitoIdentityClient(new AnonymousAWSCredentials())
val idRequest = new GetIdRequest();
idRequest.setAccountId(account-id);
idRequest.setIdentityPoolId("us-east-1:xxxx");
// If you are authenticating your users through an identity provider
// then you can set the Map of tokens in the request
val logins = new HashMap[String, String]()
providerTokens+=("www.amazon.com"->"Atza|IwE-xxxxxxxxxxxxxxxxxxx")
idRequest.setLogins(logins);
val idResp = client.getId(idRequest)
val identityId = idResp.getIdentityId()
// Create the request object
val tokenRequest = new GetOpenIdTokenRequest();
tokenRequest.setIdentityId("us-east-1:xxxxxx");
//
val tokenResp = client.getOpenIdToken(tokenRequest);
// get the OpenID token from the response
val openIdToken = tokenResp.getToken()
// you can now create a set of temporary, limited-privilege credentials to access
// your AWS resources through the Security Token Service utilizing the OpenID
// token returned by the previous API call. The IAM Role ARN passed to this call
// will be applied to the temporary credentials returned
val stsClient = new AWSSecurityTokenServiceClient(new AnonymousAWSCredentials());
val stsReq = new AssumeRoleWithWebIdentityRequest();
stsReq.setRoleArn("arn:aws:iam::account-id:role/Cognito_Auth_Role");
stsReq.setWebIdentityToken(openIdToken);
stsReq.setRoleSessionName("AppTestSession");
val stsResp = stsClient.assumeRoleWithWebIdentity(stsReq);
val stsCredentials = stsResp.getCredentials();
i get the following error:
Access to Identity 'us-east-1:xxxxxxxxxxxxxxxx' is forbidden. (Service: AmazonCognitoIdentity; Status Code: 400; Error Code: NotAuthorizedException
Following is the Cognito_Auth_Role role policy in IAM roles:
{
"Version": "2012-10-17",
"Statement":[{
"Effect":"Allow",
"Action":"cognito-sync:*",
"Resource":["arn:aws:cognito-sync:us-east-1:xxxxxxx:identitypool/${cognito-identity.amazonaws.com:aud}/identity/${cognito-identity.amazonaws.com:sub}/*"]
}]
}
And here is the trust relationships for this role:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:xxxxx"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "www.amazon.com"
}
}
}
]
}
Please point me out, what is wrong here?
Thanks
You can use AWS Cognito to handle most of the plumbing, or you can connect your own OpenId Identity Provider or a provider service you subscribe to.
Here's the docs for Identity and Access Management (IAM):
https://aws.amazon.com/documentation/iam/
Or you can have them do some of the plumbing for you with Cognito:
http://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html
While it looks like that's only for mobile, they have docs on using their JavaScript SDK in the browser:
http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/browser-intro.html

Vert.x Oauth 2 Authorization server

Can some one help me to setup Oauth 2 Authorisation server Vert.x (3.3.0).I dont find any documentation related to it.
I found vertx-auth-oauth2 this vert.x module but I guess it will be useful if Authorisation server is different
e.g
The following code snippet is from vert.x documentation
OAuth2Auth oauth2 = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, new OAuth2ClientOptions()
.setClientID("YOUR_CLIENT_ID")
.setClientSecret("YOUR_CLIENT_SECRET")
.setSite("https://github.com/login")
.setTokenPath("/oauth/access_token")
.setAuthorizationPath("/oauth/authorize")
);
// when there is a need to access a protected resource or call a protected method,
// call the authZ url for a challenge
String authorization_uri = oauth2.authorizeURL(new JsonObject()
.put("redirect_uri", "http://localhost:8080/callback")
.put("scope", "notifications")
.put("state", "3(#0/!~"));
// when working with web application use the above string as a redirect url
// in this case GitHub will call you back in the callback uri one should now complete the handshake as:
String code = "xxxxxxxxxxxxxxxxxxxxxxxx"; // the code is provided as a url parameter by github callback call
oauth2.getToken(new JsonObject().put("code", code).put("redirect_uri", "http://localhost:8080/callback"), res -> {
if (res.failed()) {
// error, the code provided is not valid
} else {
// save the token and continue...
}
});
It is using Github as Authorisation server.I am curious to know how to implement Authorisation server in vert.x ,i know spring security provides this feature i.e Oauth2Server and OAuth2Client.
Vert.x OAuth2 is just a OAuth2Client, there is no server implementation so you cannot get it from the Vert.x Project itself.
Vert.x OAuth2 supports the following flows:
Authorization Code Flow (for apps with servers that can store persistent information).
Password Credentials Flow (when previous flow can’t be used or during development).
Client Credentials Flow (the client can request an access token using only its client credentials)

Categories