I have a React SPA which uses msal. I have configured Azure AD as Identity Provider for my AADB2C. I can signIn/signOut and other operations.
If my user signs out off my application, I want to also sign out of my Identity Provider. I looked a bit into this matter 1, 2, 3, 4, 5.
At this moment, I use msal's logoutRedirect method.
const url = new URL(AadEndSessionEndpoint);
url.searchParams.append('post_logout_redirect_uri', SPAUrl);
instance.logoutRedirect({
postLogoutRedirectUri: url.toString()
});
What happens, after my user signs out of my AADB2C, he gets redirected to the AAD end_session_endpoint. I can sign out there as well, but my user gets stuck there. Even though I'm passing the post_logout_redirect_uri query parameter to go back to my app, it ignores it.
How could I make this work?
You are doing an RP Initiated Logout in OpenID Connect terms, meaning you need to also send the id_token_hint query parameter.
I can also confirm that sending both query string parameters logs out successfully for my Azure developer account:
url.searchParams.append('post_logout_redirect_uri', SPAUrl);
url.searchParams.append('id_token_hint', myIdToken);
I think the MSAL library requires you to use getAccount instead:
const account = msalInstance.getAccount();
await msalInstance.logoutRedirect({
account,
postLogoutRedirectUri: "https://contoso.com/loggedOut"
});
UPDATE
Your code above is not right - the post logout redirect URI should be that of your own app - I expect the library already knows the end session endpoint location - so just do this:
instance.logoutRedirect({
postLogoutRedirectUri: SPAUrl
});
At the same time it is worth being aware that the full standards based GET URL should look like this. With the knowledge of the expected URL you can check that you are sending the right request via browser tools:
https://[AadEndSessionEndpoint]?id_token_hint=[myIdToken]&post_logout_redirect_uri=[SPAUrl]
The end session endpoint should be a value such as this by the way:
https://login.microsoftonline.com/7f071fbc-8bf2-4e61-bb48-dabd8e2f5b5a/oauth2/v2.0/logout
Related
I am using Azure mobile app services with Xamarin Forms.
In my app, I use web social media authentication (Facebook, Twitter, Google) configured in the azure portal.
I am taking the sid gotten from CurrentClient.Id to match it with users in my Easy Tables. However, for some users, after logging in with the same account and same provider, no match is found in my database because the sid is different! I am 100% sure that it is the same account used to login before, yet I get a different sid. How is that possible? Shouldn't it remain the same with every login or what's the whole point of it then?
You are using Azure App Service Authentication for this. There is a stable ID that is available within the JWT that you pass to the service. You can easily get it from the /.auth/me endpoint (see https://learn.microsoft.com/en-us/azure/app-service/app-service-authentication-how-to#validate-tokens-from-providers )
When you GET /.auth/me with the X-ZUMO-AUTH header set to the authenticationToken returned from the login, the user.userId field will be populated with a stable ID. So, the next question is "how do I add this / compare this within the Node.js backend?" Fortunately, the HOW-TO FAQ for Node.js explicitly answers this. Short version is, use context.user.getIdentity() (an async method) to get the identity, then do something with it:
function queryContextFromUserId(context) {
return context.user.getIdentity().then((data) => {
context.query.where({ id: data.userId });
return context.execute();
});
}
function addUserIdToContext(context) {
return context.user.getIdentity().then((data) => {
context.itme.id = data.userId;
return context.execute();
});
}
table.read(queryContextFromUserId);
table.insert(addYserIdToContext);
table.update(queryContextFromUserId);
table.delete(queryContextFromUserId);
The real question here is "what is in the data block?" It's an object that contains "whatever the /.auth/me endpoint with the X-ZUMO-AUTH header produces", and that is provider dependent.
The mechanism to figure this out.
Debug your client application - when the login completes, inspect the client object for the CurrentUser and get the current token
Use Fiddler, Insomnia, or Postman to GET .../.auth/me with an X-ZUMO-AUTH header set to the current token
Repeat for each auth method you have to ensure you have the formats of each one.
You can now use these in your backend.
I have registered an app in Azure for Microsoft Identity platform. I configured it to allow MS Accounts (e.g. outlook.com) and have basically done everything in a few of the quickstarts online here and here (except for "add credentials to your web app"). I have also checked the boxes that enable implicit flow.
I redirect my React application to the URL to sign in (using implicit flow), I get to enter my username but then I see
unauthorized_client: The client does not exist or is not enabled for consumers. If you are the application developer, configure a new application through the App Registrations in the Azure Portal at https://go.microsoft.com/fwlink/?linkid=2083908
Like I mentioned above, I've gone through several quick starts and read about implicit flow here and followed their examples for my code.
I also tried just deleting the app registration and starting over. No luck.
JS Code attempting to implement Implicit Flow
JS code that redirects the browser to a Url that looks like Microsoft's first example on their implicit flow page
goSignIn() {
const tenant = 'common'; // (for us with MS accounts)
const clientId = '*****';
const redir = encodeURIComponent('http://localhost:3000/signin');
const nonce = Math.round(Math.random() * 10000, 0);
const uriTemplate = 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?client_id={clientId}&response_type=id_token+token&redirect_uri={redirect}&scope=openid&response_mode=fragment&state={state}&nonce={nonce}';
const filledTemplate = uriTemplate
.replace('{tenant}', tenant)
.replace('{clientId', clientId)
.replace('{redirect}', redir)
.replace('{nonce}', nonce)
.replace('{state}', nonce);
console.log(filledTemplate);
window.location = filledTemplate;
}
App Configuration in Azure:
Azure -> Identity -> App Registrations -> MyApp -> Authentication
Redirect Uri: http://localhost:3000/signin (React app runs on 3000 and I have a route configured for /signin)
Not using any suggested Redirects.
Checked Implicit checkboxes for ID Token and Access Token
Live SDK support enabled
Supported account types is set to "Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com)"
Azure -> Identity -> App Registrations -> MyApp -> API Permissions
MS Graph
User.Read
Email
Profile
openid
From the docs I read, I thought I had done enough to the id token. I'm not sure what tweak must be made in order to get it to work.
I experienced an issue like this one. The mistake I made has to do with the App ID: when you create the client secret the Azure UI will present the secret and the secret ID. This secret ID is not the one to use in your app's configuration. Rather, you need the Application ID found on the Overview page.
I imagine that there are many configuration problems which can produce this error message. In general: pay close attention to the App ID, if the error is that the app is not found.
It seems that you have done enough to get the token. I have tested this on my side, it works well. Here I provide you with my screenshot for you to check again.
Also, here is my working request url, you can login with your msa to have a test.
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=5fd66168-7ba3-4bbc-a155-bff662eed9f7
&response_type=id_token+token
&redirect_uri=http://localhost:3000/signin
&scope=openid
&response_mode=fragment
&state=12345
&nonce=678910
In an old application some people in my company were able to get info from Microsoft Graph without signing users in. I've tried to replicate this but I get unauthorized when trying to fetch users. I think the graph might have changed, or I'm doing something wrong in Azure when I register my app.
So in the Azure portal i have registered an application (web app), and granted it permissions to Azure ad and Microsoft graph to read all users full profiles.
Then I do a request
var client = new RestClient(string.Format("https://login.microsoftonline.com/{0}/oauth2/token", _tenant));
var request = new RestRequest();
request.Method = Method.POST;
request.AddParameter("tenant", _tenant);
request.AddParameter("client_id", _clientId);
request.AddParameter("client_secret", _secret);
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("resource", "https://graph.microsoft.com");
request.AddParameter("scope", "Directory.Read.All");
I added the last row (scope) while testing. I still got a token without this but the result is same with or without it.
After I get a token I save it and do this request:
var testClient = new RestClient(string.Format("https://graph.microsoft.com/v1.0/users/{0}", "test#test.onmicrosoft.com")); //I use a real user here in my code ofc.
testRequest = new RestRequest();
testRequest.Method = Method.GET;
testRequest.AddParameter("Authorization", _token.Token);
var testResponse = testClient.Execute(testRequest);
However now I get an error saying unauthorized, Bearer access token is empty.
The errors point me to signing users in and doing the request, however I do not want to sign a user in. As far as i know this was possible before. Have Microsoft changed it to not allow anonymous requests?
If so, is it possible to not redirecting the user to a consent-page? The users are already signed in via Owin. However users may have different access and i want this app to be able to access everything from the azure ad, regardless of wich user is logged in. How is the correct way of doing this nowadays?
Or am I just missing something obvious? The app has been given access to azure and microsoft graph and an admin has granted permissions for the app.
Edit: just to clarify, i tried both "Authorization", "bearer " + _token.Token, and just _token.Token as in the snippet.
Yes, it's still possible to make requests to Graph without a user present using application permissions. You will need to have the tenant admin consent and approve your application.
Edit / answer: Adding the 'Authorization' as a header instead of a parameter did the trick. It works both with 'bearer token' and just 'token'
I am using the following bits against my Azure AD to authenticate with ASP.NET Core.
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-openidconnect-aspnetcore/
https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore
I have the basic login/auth working after creating an Azure AD app. User can login/logout.
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
Also, is it possible to retrieve a bearer token via this approach? Or does this require another type of call or extending "scope"? So that for example I could retrieve the authenticated users Manager.
https://graph.microsoft.com/v1.0/me/manager
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
This way only able to log those who already sign-in your app successfully. It is not able to log those users who are attempt to sign-in your app but enter the wrong password.
Azure AD already provide lots of report to gain visibility into the integrity and security of your organization’s directory.( refer here)
And if you are using the Azure AD Premium, you can review the sign-in activities via the Azure new portal below:
And if you want to store the sign-in activity in your web app, you can write the custom code after the token is verified. Here is the code for your reference:
// Configure the OWIN pipeline to use OpenID Connect auth.
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = Configuration["AzureAD:ClientId"],
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAd:Tenant"]),
ResponseType = OpenIdConnectResponseType.IdToken,
PostLogoutRedirectUri = Configuration["AzureAd:PostLogoutRedirectUri"],
Events = new OpenIdConnectEvents
{
OnRemoteFailure = OnAuthenticationFailed,
OnTokenValidated = context => {
//write the custom code to store users login-in
return Task.FromResult(0); }
},
});
Also, is it possible to retrieve a bearer token via this approach?
Yes. We can get the token after receive the authorization code. You can refer the code sample here to acquire the token from asp.net core app.
I am trying to create an application to browse my contacts directory on Exchange.
I have set up everything and I am able request the authorization from my app.
I can present the modal view, enter the login information, retrieve the token, but when I try to authorize the app with the same account I have created it I get this message:
The client <my app id> and resource <my app URI> identify the same application.
If I try to authorize another account, I receive this message instead:
User account <an email> from external identity provider <a url> is not
supported for application <my app id>
If I try to login on the Graph Explorer Console or on the Office 365 OAuth Sandbox, they work fine with the second address, but not with the first one.
I am really confuse. I feel like I have mess up some configuration option, but I don't really understand which one.
Regarding #1, please do not pass App ID of your application for resource querystring parameter when authenticating against your tenant URL. I ran into the exact same problem.
Then I ran WebApp-MultiTenant-OpenIdConnect-DotNet from Github and noted down the sign-in URL it created and I used the following:
var signInUrl = String.Format(
"https://login.windows.net/{0}/oauth2/authorize?response_mode=form_post&response_type=code+id_token&scope=openid+profile&client_id={1}&resource={2}&redirect_uri={3}&state={4}&nonce={5}",
Uri.EscapeDataString(tenantId),
Uri.EscapeDataString(clientId),
Uri.EscapeDataString("https://graph.windows.net"),
Uri.EscapeDataString(redirectUri),
Uri.EscapeDataString(state),
string.Format("{0}{1}", DateTime.UtcNow.Ticks, Guid.NewGuid().Stringify())
);
Basically I used https://graph.windows.net instead of App ID and magically things started to work :).
Another thing you could try (and I have not tried it) is authenticating against common endpoint https://login.windows.net/common/oauth2/authorize and provide your App ID for the resource querystring.