RabbitMQ authentication using Azure ADB2C tokens is not working - java

I am trying to use Azure AD as OAuth server for RabbitMQ authentication. For this i want to use Azure ADB2C tokens.
I have enabled RabbitMq's oauth2 plugin. And i want to use signing key as public private key setup.
Few details regarding setup i have before reaching to this question...
1. I have Azure ADB2C setup to authenticate my users to our REST API in application. This works fine.
2. I tried to authenticate RabbitMQ with open source OAuth 2 server like UAA, this works fine too.
Idea is to use JWT token that Azure ADB2C generates to authenticate & authorize users to the RabbitMQ. server.
I have private key from Azure ADB2C User Flow generated Metadata document JWKS URI. So i used this JWKS URI - JWT token to generate public key.
I have provided the above generated public key to RabbitMQ config as follows
{rabbitmq_auth_backend_oauth2, [
{resource_server_id, <<"rabbitmq">>},
{key_config, [
{default_key, <<"key-1">>},
{signing_keys,
#{<<"key-1">> => {pem, <<"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVKUtcx/n9rt5afY/2WF
NvU6PlFMggCatsZ3l4RjKxH0jgdLq6CScb0P3ZGXYbPzXvmmLiWZizpb+h0qup5j
znOvOr+Dhw9908584BSgC83YacjWNqEK3urxhyE2jWjwRm2N95WGgb5mzE5XmZIv
kvyXnn7X8dvgFPF5QwIngGsDG8LyHuJWlaDhr/EPLMW4wHvH0zZCuRMARIJmmqiM
y3VD4ftq4nS5s8vJL0pVSrkuNojtokp84AtkADCDU/BUhrc2sIgfnvZ03koCQRoZ
mWiHu86SuJZYkDFstVTVSR0hiXudFlfQ2rOhPlpObmku68lXw+7V+P7jwrQRFfQV
XwIDAQAB
-----END PUBLIC KEY-----">>}
}
}]}]}
I have created an AD user in Azure ADB2C and assigned it to RabbitMQ app registered over Azure. I have provided default scopes to the RabbitMQ app registered with rabbitMQ-permissions like rabbitmq.read... etc. and I have provided default API permission to App registered.
Now in my java application i am trying to make connection to RabbitMQ with following code
CredentialsProvider credentialsProvider =
new OAuth2ClientCredentialsGrantCredentialsProviderBuilder()
.tokenEndpointUri("http://****/B2C_1_RabbitMQ/oauth2/v2.0/token")
.clientId("rabbit_client_from_Azure").clientSecret("rabbit_secret_from_Azure")
.grantType("password")
.parameter("username", "rabbit_admin_from_Azure")
.parameter("password", "rabbit_admin_from_Azure")
.build();
connectionFactory.setCredentialsProvider(credentialsProvider);
Connection connection = connectionFactory.newConnection(); // Line 1
When i run above code, Line 1 it gives me error as below
Exception in thread "main" com.rabbitmq.client.impl.OAuthTokenManagementException: HTTP request for token retrieval did not return 200 response code: 400
I am not sure why i am getting above error. I initially thought it would be a matter of scopes/permission from Azure ADB2C registered RabbitMQ app, but i am not able to figure out what i am missing here.
Any help here is much appreciated.

I have gotten this working by using jwks_url in key_config instead of adding the keys themselves to the config like this:
{key_config, [
{jwks_url, <<"https://login.microsoftonline.com/your tenant id/discovery/v2.0/keys">>}
]}
Just replace "your tenant id" with the id for your tenant.

Related

Embedding AWS IAM credentials in the code with the Java SDK

I am using the Java AWS IoT SDK, and i'm I'm stuck with a problem whereby I have to embed my AWS IAM access key and secret key credentials into my Java application code on my devices.
The credentials are just used initially to create the client in my code, then X.509 certificates are used after for the MQTT authentication and communication. .
I've heard of a way to avoid the need of embedding IAM credentials in the code by using AWSCredentialsProvider with tokens etc. However, I don't see any actual examples of how to achieve this without embedding credentials. Below is a snippet of my code showing how I create the client object using the credentials. Thanks.
String AWS_ACCESSKEY = "AKXXXXXXXXXXXXX"; // not real key
String AWS_SECRETKEY = "ABCXXXXXXXXXXXXXXXXXXXXXX"; // not real key
Regions AWS_REGION = Regions.US_EAST_2;
AWSIot client = AWSIotClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(new
BasicAWSCredentials(AWS_ACCESSKEY, AWS_SECRETKEY))).withRegion(AWS_REGION).build();
You can pass this credentials to normal application.properties file.
You just need to do 2 things.
Create public class AwsCredentials with annotations #ConfigurationProperties and #Configuration.
Pass Your access and secret to application.properties file
You can read more in this tutorial : click
Next when You want to use this properties in builder You need to call it like this:
AWSIot client = AWSIotClientBuilder.standard()
.withCredentials(
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(
this.awsCredentials.getAccessKey(),
this.awsCredentials.getSecretKey()
)
)
)
.withRegion(AWS_REGION)
.build();
PS. You can export region to properties too.
You can use temporary security credentials instead of actual access keys. Do check this link.
https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html
To get credentials to access AWS IoT (or other services) you could get temporary security credentials from Cognito Identity Pool. You can find the simplest way and steps needed to do get credentials here.
Also consider that, to get idToken (JWT) from Cognito user pool and then access and secret token Cognito Identity pool, you need to use AWS Java SDK in your mobile or desktop application. You can find more information about AWS JAVA SDK here and some samples here, here, .

AWS Cognito GetCredentialsForIdentity

I am trying to use the approach outlined in the following blog article to authenticate a cognito identity to S3 from Java:
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-4-enhanced-flow/
I have successfully implemented the developer authentication provider and can retrieve a valid OpenId token for my cognito identity. That token works in both the iOS and Android SDKs when uploading/downloading files to S3. Unfortunately, I have not been able to successfully authenticate the same identity to S3 in my Java app. Here is the code I am using:
Map<String, String> logins = new HashMap();
logins.put("cognito-identity.amazonaws.com", cognitoOpenIdToken);
GetCredentialsForIdentityRequest getCredentialsRequest =
new GetCredentialsForIdentityRequest()
.withIdentityId(cognitoIdentityId)
.withLogins(logins);
AmazonCognitoIdentityClient cognitoIdentityClient =
new AmazonCognitoIdentityClient();
GetCredentialsForIdentityResult getCredentialsResult =
cognitoIdentityClient.getCredentialsForIdentity(getCredentialsRequest);
The call to getCredentialsForIdentity fails with the error "com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain". This method is documented as a public API that does not require authentication. However, I have noticed that the call succeeds if done from an environment where there are valid IAM credentials configured in the system variables. It fails in environments where that is not the case.
Am I missing something simple?
Have you tried initializing the AmazonCognitoIdentityClient in following manner?
new AmazonCognitoIdentityClient(new AnonymousAWSCredentials());
Otherwise, by design of AWS Java SDK, the service clients look for AWS credentials, because same client can be used for accessing Authenticated APIs as well.
maybe is not that you need to be as another IAM user... but that the user that is running you method has a "deny" on sts:* (or some other actions that you need)

AWS MQTT Websocket, authenticated access being restricted

I'm currently working on an AWS mobile app project in Android Studio, and I'm currently trying to set up a MQTT-connection to AWS IoT through AWS Cognito & IAM authentication.
If I run the MQTT-client as a stand-alone project without authentication, assuming an unauthenticated IAM role, I'm able to connect to MQTT, and subscribe & recieve from the given topic.
However, once I try to run my MQTT-client through an authenticated user (through a log-in on my mobile app), the client is restricted, and constantly tries to reconnect.
I enabled logging in AWS CloudWatch, and the following debug message is given:
"... EVENT:MQTT Client Connect MESSAGE:Connect Status: AUTHORIZATION_ERROR"
The unauth and auth roles in the IAM console have identical policies & resource access, yet something's stopping the MQTT connection.
PS. The Auth role can, in my app, access userfiles & S3, the probelem seems to be unique for IoT access.
Does anyone know if it's possible to allow connections through an authorized user without using cert-files, if so, how? Unless I'm mistaken Cognito and IAM should be able to perform the required authorization to access resources (and it does, as long as I'm not logged in to the auth role)
Appreciate any tips I can get at this point, been struggling for a while.
I eventually figured out the issue by reading the AWS documentation more thoroughly.
The AWS credentials provider does not store the required information to authorize access through AWS. So I solved the issue by retreiving the required user tokens into a hash, and setting these to the credentials provider, and the issue was finally solved.
AuthenticationResultType authenticationResultType = new AuthenticationResultType();
String idToken = authenticationResultType.getIdToken();
// Initialize the AWS Cognito credentials provider
credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
COGNITO_POOL_ID,
MY_REGION
);
Map<String, String> logins = new HashMap<String, String>();
logins.put("yourAWSEndpoint", idToken);
credentialsProvider.setLogins(logins);

Multiple SSO providers in Spring Boot Auth server

I have read and implemented my own Auth server following this tutorial from Spring. There are multiple SSO providers - Facebook, Github and a custom auth server. In this tutorial, the auth server contains the handling of other SSO providers.
I have a separate resource server that links to my auth server using the following properties:
security.oauth2.resource.userInfoUri=http://localhost:9000/user
I am able to get the token from my auth server using a cUrl command:
curl acme:acmesecret#localhost:9000/oauth/token -d grant_type=password -d username=user -d password=...
{"access_token":"aa49e025-c4fe-4892-86af-15af2e6b72a2","token_type":"bearer","refresh_token":"97a9f978-7aad-4af7-9329-78ff2ce9962d","expires_in":43199,"scope":"read write"}
But what I fail to understand is how can I use the other SSO providers to get such token as well from the auth server? The resource server should not care how did I get the token and whether I am authenticated using Facebook or my custom auth server. It should simply ask the auth server what is the Principal (logged user) and then decide which resources to show him, right?
I don't have any UI and this will be backed for a mobile application so I need to udnerstand how to handle the authentication using REST reqeusts.
If I understand your question correctly,
how can I use the other SSO providers to get such token as well from
the auth server?
This custom Auth server is abstracting out your interaction with FB or Github and issuing you it's own token. The token that your custom Auth server spitting out is not an FB or Github token, it's a token generated by your custom Auth server (After authenticating with FB/Github token).
Then why do we need FB/github?
How else your custom Auth server can identify a person, It sure can use user Id and Password; consider 'login with FB' as another nice option it gives to the user.
How to add other SSO providers like digitalocean in addition to FB and github?
Just do the same as we did for FB and Github (register a client id with digital ocean and then in auth server application, Add client Id and secret in the properties/yaml file etc)
The resource server should not care how did I get the token and
whether I am authenticated using Facebook or my custom auth server. It
should simply ask the auth server what is the Principal (logged user)
and then decide which resources to show him, right?
Yes, your understanding is correct.
Edit (To answer question asked in the comment)
But lets say I log in with Facebook through my Auth server. Where do I
find the token that I can use with the Resource server? Let's say I
have a RestClient and want to make a request to obtain some resource
belonging to a user which went through the Facebook auth process via
my auth server. Where do I find the token to use?
If that's a requirement, I think you can use this example instead; you may not need a custom auth server as such. Whole point of having custom auth server is abstracting out the interaction with FB or github.
Or
If you still want to go with custom auth server direction, then expose an endpoint from Auth server (which will get you the resources you need from FB) and then make use of that from your resource server.

How to configure spring oauth?

I have 2 web applications:
Resource Server (#EnableResourceServer)
auth app (#EnableAuthorizationServer)
They are mapped to one database.
I would like to split database for 2: one for client app and the second one for tokens.
Question: How client app should be configured correctly with oauth?
My current flow:
Resource Server get request with token
spring security checks token in database
May be the best flow is to isolate auth app and database and flow should be something like this:
Resource Server get request token
and Resource Server makes a request to auth web app (OAuth Server) to verify token
auth app (OAuth Server) spring security checks token in database
?
Let me know if I misunderstand any point about oauth. Thanks.
If you want use the auth server for checking tokens you need a RemoteTokenServices (or the equivalent). If the server is a Spring Oauth sever (using #EnableAuthorizationServer) there should be a /check_token endpoint.
N.B. It might be a good idea to read the spec and get the terminology straight (your "client app" is a "resource server".

Categories