aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.44k stars 2.13k forks source link

AWS Amplify SPA React + Cognito (Microsoft Azure Ad Enterprise SSO Enabled ) + Microsoft Graph API #4100

Closed nickchauhan closed 5 years ago

nickchauhan commented 5 years ago

Which Category is your question related to? Auth What AWS Services are you utilizing? Cognito

Provide additional details e.g. code snippets I'm using React for developing a single page application and AWS Amplify for serverless integration. To Sign-in the user, I have successfully configured the AWS Cognito console with SAML so that our organization users can log in the application without register and can access the AWS resources. We are using Microsoft Azure Ad as the Identity Provider.

Now inside the application, I have to implement a search that can query the Azure AD users using the Microsoft Graph API.

GET https://graph.microsoft.com/v1.0/users?$filter=startswith(displayName,'J')

We have the appropriate permission for accessing the graph. We have the client ID, secret ID & we have enabled implicit grant flow for the App in Azure.

Since I'm using AWS Amplify and Cognito to authenticate the user, I receive IdToken and accessToken when the user login into the application.

const user = await Auth.currentAuthenticatedUser();
const idToken = user.signInUserSession.idToken.jwtToken;
const accessToken = user.signInUserSession.accessToken.jwtToken;

But this id-token and access-token is of Cognito and not of Microsoft Azure AD.

Microsoft provides MSAL.js (for OAuth 2 implicit grant flow) for making graph API calls on SPA but we cannot use that in our application since we are using AWS Amplify (Cognito) for authentication.

So How to make graph calls with the help of AWS Lamda or any other AWS services. It would be really helpful if you recommend any other approach.

haverchuck commented 5 years ago

@nickchauhan - How would you normally go about authorizing these calls to the MS Graph API? What kind of credentials are typically needed?

nickchauhan commented 5 years ago

@haverchuck There are 2 methods to call the Graph.

  1. OAuth 2.0 Implicit grant flow - This flow can be implemented using the MSAL.js but it redirects to Sign in Page or opens up a popup window, which we don't want because of we are already authenticating the user by Cognito.
  2. OAuth 2.0 client credentials flow - If we go with this approach we have to make server-side calls I don't understand how to approach this using AWS Amplify ( Using REST API ) or AWS Lambda function or any other AWS method. (making the admin consent call, responds admin_consent values to the reply URL, how to take care of that)

Thanks

haverchuck commented 5 years ago

@nickchauhan So how exactly are you attempting to call this graph right now? What kind of headers or request params do you need to send to authorize it (authorize, not authenticate)? Is that where it's execting an access token from Azure?

nickchauhan commented 5 years ago

As we are already signing in the user via Cognito I tried to call the /token endpoint to fetch token. (I understand that it is not the correct way of doing it. We thought that the accessToken and IdToken we have in the Auth is of Microsoft Azure but now I understand that it's of Cognito)

const user = await Auth.currentAuthenticatedUser();
const idToken = user.signInUserSession.idToken.jwtToken;
const tokenResponse = await Axios.post(`https://login.microsoftonline.com/${APP.TENANT}/oauth2/v2.0/token`,
`client_id=${APP.CLIENT_ID}&client_secret=${APP.SECRET}&scope=user.read&code=${idToken}&grant_type=authorization_code`,
   {
     headers: {
         'Content-Type': 'application/x-www-form-urlencoded',
      },
     }
   );

Now I have tried the OAuth2 client credential flow in Postman but redirects me again to the sign-in page and admin has to approve it before I call the /token api and than the graph api.

I'm not able to understand which OAuth method should I choose . If I go with OAuth 2.0 Implicit grant flow than It shows a popup window to Sign In and I have to use the MSAL.js for it but that would not be helpful since I'm using Cognito to sign in. If I go with OAuth 2.0 Client Credential flow it again uses sign in screen for the admin approval and I don't understand how should I do this with AWS Amplify or Lamda or other services.

haverchuck commented 5 years ago

@nickchauhan - Let me confirm that I understand the situation:

  1. You have configured Cognito (I am assuming a Cognito User Pool, not Identity Pool) to allow federation with SAML, with Azure AD as the idenity provider.

  2. You can successfully login, and receive tokens from Cognito.

  3. Now, you need to query AD for user data.

Assuming this is correct, then the problem is that you have tokens from a Cognito User Pool which you can give to a Cognito Identity Pool in exchange for temporary credentials to access AWS resources (Amplify can handle that for you); those credentials, however, are probably useless for directly accessing resources from another cloud provider like Azure.

However, one thing you might want to explore is creating an AWS resource such as a Lambda which itself has appropriate credentials to query for Azure AD data. In other words, your users wouldn't have permissions to access AD, but they would have permissions to access a protected resource which would itself have permissions. Perhaps it is not really necessary to 'login' in order to access this Azure AD data... maybe you could access it with a secret key or something like that? I am not an expert on Azure AD so I can't really say for sure.

stale[bot] commented 5 years ago

This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.

ckieran commented 5 years ago

would it be better for you to authenticate direct to Azure AD rather than via cognito? Do you have a requirement to use cognito to act as an intermediary user store? eg are you adding custom claims to your users once they are added to the cognito user pool via AD authentication? If not then removing cognito from the mix might clear things up a bit.

Azure AD seems to support OAuth2 authentication flows https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code

You mention MSAL.js but I can see mention of ADAL.js as a client side library for auth direct to Azure AD in that blog post above.

There's an SPA sample here: https://github.com/Azure-Samples/active-directory-javascript-singlepageapp-dotnet-webapi

I am in a similar boat where I am integrating with cognito to farm out to Azure AD as a SAML idP and I'm wondering what there is to gain from it... in my case we will probably have App specific groups and permissions that we might want to store in the cognito user store which would be onerous to manage in AD as none of the dev team has access to do that, but your situation might be different...

prathik-naidu commented 4 years ago

@ckieran having the same issue here trying to figure out how to get the right tokens for accessing the MS Graph API through Cognito. Were you able to figure out how to do this or did you not end up using Cognito?

JReinhold commented 4 years ago

I've been down the same path, at came to the conclusion that it is not possible to get the original access token from a federated identity. I've come up with two workarounds:

  1. Have a server side function (eg. Lambda) with client_credentials, that the frontend calls. Using client_credentials means that the function uses Application wide permissions, and not permissions on behalf of a user, and therefore the function is capable of requesting its own token, without communicating with Cognito. Choosing this approach means that you'll have to make sure the server-side function is behind authentication (eg. AWS IAM), or else you risk exposing the full Graph API to bad actors.
  2. For simple user data, you can include optional claims in the id token issued between Azure AD and Cognito, and then map those claims to the user attributes in the Cognito User Pool. This doesn't give you the ability to request the Graph API, but if you just need to extend the user with simple extra data (like country, og group memberships), this is a viable approach. You can't use this approach to get more advanced data like the user's photo or similar. (here's a list of the possible additional claims available)

I've ended up using the second approach, because my Azure admin didn't want to give me Applicaton permissions, but only User Delegated permissions - so if anyone finds a way to get the access token from a federated identity, please let me know!

gilsgstav commented 4 years ago

Maybe the other way around would work: having a process in AD subscribe to a change in AWS and respond to that. - just a thought..

github-actions[bot] commented 3 years ago

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels or Discussions for those types of questions.