I'm currently using Azure B2C to authenticate a series of .NET 6 server-side Blazor apps and Web APIs. In IIS, the apps are setup as follows:
domain.com
domain.com/admin
domain.com/api1
domain.com/api2
domain.com/api3
If a user logs into domain.com and then navigates to domain.com/admin, /admin gets caught in an infinite authorize loop with Azure B2C. Then, if you browse back to domain.com and logout, you can navigate back to /admin and currently be signed in.
However, if you log into /admin first and navigate to domain.com, you will be successfully logged in. Then, if you navigate back to /admin, you get stuck in a auth loop again.
I have verified that the only difference between the 2 access tokens is the audience and the timestamps issued (of course). In B2C, I have both web applications set to accept access tokens from any app authorizing with the B2C domain.
I have also tried setting up my cookies with settings from here: Cookie Sharing.
Any help/guidance I can get would be greatly appreciated.
EDIT 1:
Logging into domain.com then logging into /admin causes the loop. Then, if I log out of domain.com and navigate back /admin, the loop stops and I'm authenticated.
Dev tools show zero errors and from what I can tell in code, it appears like I am authenticated.
EDIT 2:
I noticed that some of the B2C samples had the following code that I was missing:
services.Configure<CookiePolicyOptions>(options =>
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.HandleSameSiteCookieCompatibility();
});
I added this code and that seems to have fixed the login loop portion. However, when I call the following:
await TokenAcquisition.GetAccessTokenForUserAsync(new[] { ApiSettings.UserScope });
I receive the following error:
False MSAL 4.40.0.0 MSAL.NetCore .NET 6.0.3 Microsoft Windows 6.3.9600 [2022-04-20 15:01:50Z - 31ac7d51-15e1-424a-8be1-1eb09a6cbf82] Exception type: Microsoft.Identity.Client.MsalUiRequiredException
, ErrorCode: user_null
at Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
I am attempting to obtain a token for calling a downstream API.
Related
I'm trying to add authentication for the nuxt application through IDP federation with azure AD.
basically nuxt auth calls to Azure B2C tenant and it federate to the Azure AD.
The process as follows.
Nuxt Auth -> Azure B2C -> Azure AD (Federated IDP)
Azure AD returns the token as the result of federation.
Azure B2C add that token to the ID token under the key idp_access_token. Then Nuxt auth get the token and use it for authentication.
But after successful login it expect to redirect to the /home page.
redirect: {
login: "/",
callback: "/",
home: "/home"
},
while it redirect it shows the 431 error. The reason for that is large cookie that contains
access token, refresh token
Instead of redirect to the given home page it gives an error like following screenshot shows.
error shows like follows.
is there any way to solve this issue?
I upgraded node version from 12 to 15, deleted browser cache and cookies. But issue is still there.
For 431 error code, we need to change the max size of http request header, use command
--max-http-header-size
and here's an answer related.
I have a Blazor Hosted WASM application, and am using Azure AD-B2C to secure it. If a user who is not logged in tries to access any site on the page, they are directed to our b2c login page, as they should be, and if they supply a good username and password they are allowed to view the site. So far so good. However, if the user clicks on "Sign up now", and then cancels the signup process instead of providing a new username, password, and e-mail address, then they are redirected to the site's landing page (as if they had provided a good username and password), which fails to redirect them back to the b2c login page and produces a console message reading "info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user"
The documentation suggests that I access the app's manifest and set the allowPublicClient attribute to null or true to address this problem. I have done this, and the problem persists. Why isn't the user being redirected back to the B2C login page in this case, when they normally would be if they try to access any page on the site (including this landing page) in other cases?
When the user cancels and get redirected back to the landing page, it returns an specific error code in the url (eg: AADB2C90118) in the return redirect url (In some cases it only flashes quickly because Angular removes the query string after the redirect).
You need to listen to this and handle it. You can handle it manually by parsing the return url but if you use msal.js on client side you can listen to this and start the reset password flow.
this.broadcastService.subscribe("msal:loginFailure", (error) => {
console.warn("msal:loginFailure", error);
// Check for forgot password error
// Learn more about AAD error codes at https://learn.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes
if (error.errorMessage.indexOf("AADB2C90118") > -1) {
alreadyRedirecting = true;
this.msalAuthService.loginRedirect(
b2cPolicies.authorities.resetPassword
);
}
});
The above example is for when the user clicks on forgot password link and get redirected back, so you will need to find the error code that applies to you.
I've been following the guide in this repo to setup a resource token provider as an Azure function.
https://github.com/adamhockemeyer/Azure-Functions---CosmosDB-ResourceToken-Broker
I've setup the Cosmos DB, App registration, and Azure function with permissions according to the instructions. When I get to step 3 in the guide where I enter https://{function-url}/.auth/login/{provider} into my browser I get the following error.
ADSTS50011: The reply url specified in the request does not match the
reply urls configured for the application: [App id]
Update
I changed the Reply URL in my App Registration to https://{function-url}/.auth/login/{provider}/callback with {function-url} as my Function App URL. Originally I had it set to one of my Function URL's. Now i'm getting a 404 not found error.
App Registration Redirect URL
Function App URL
Function App Authentication Settings
You need to add the URL as below in your app registration, then it should work.
Note: After adding the URl, go back to the Authentication / Authorization in your functionapp, turn off the Authentication Providers whcih you configured -> turn off Authentication / Authorization, then turn on and configure again, otherwise it seems not become effective.
https://{function-url}/.auth/login/{provider}/callback
Update:
Turn off all the configuration and create the new AD App like below.
I am developing a chrome extension which requires authentication through a non-google service. It is currently set to unlisted in the Chrome Web Store
The expected flow is as follows:
User opens the extension
Extension check for an authentication code
If the code does not exist, use launchWebAuthFlow to start authentication with the service
User authenticates with the service
Service redirects to the extension with the authentication code
Code is extracted and the user is authenticated
Up to step 4 is working fine, but when redirecting back to the extension, I keep getting a DNS
'appId'.chromiumapp.org’s server DNS address could not be found.
Try running Windows Network Diagnostics.
DNS_PROBE_FINISHED_NXDOMAIN
'appId' is the ID for the chrome extension. My manifest.json contains the permission for the service url:
...
"permissions": [
"*//<service-provider-url>/*"
]
...
The call to the auth flow looks like this:
chrome.identity.launchWebAuthFlow({'url':'<URL for auth>', 'interactive' : true}, function(responseUrl){
// stuff happens here
});
The callback url is registered with the provider and, as far as understand, is being called correctly to return the extension, according to the docs:
This method enables auth flows with non-Google identity providers by
launching a web view and navigating it to the first URL in the
provider's auth flow. When the provider redirects to a URL matching
the pattern https://.chromiumapp.org/*, the window will close,
and the final redirect URL will be passed to the callback function.
Is there something I might be missing during set up?
I'm implementing my own identity provider based on Thinktecture code. Here is a strange behaviour of Azure ACS while using a single sign-out feature, it differ for google/live and for my own identity provider.
URL for sign-out (realm is really same as a site name):
mysite.accesscontrol.windows.net/v2/wsfederation?wa=wsignout1.0&wreply=http%3a%2f%2flocalhost%2fAdministration.Frontend.Web%2f&wtrealm=http%3a%2f%2flocalhost%2fAdministration.Frontend.Web%2f
Here is a pseudo-code for logout:
//clear FedAuth cookies
FormsAuthentication.SignOut();
FederatedAuthentication.WSFederationAuthenticationModule.SignOut(true);
//call Single SignOut
var signoutRequestMessage = new SignOutRequestMessage(new Uri(signOutUrl));
return Redirect(signoutRequestMessage.WriteQueryString());
Here is sample flow (i'm using private browsing plus Fiddler to see everything):
1) I'm logging into my application with google account.
2) click a logout, in result i get a page on ACS with a this code:
function on_completion()
{window.location = 'http://localhost/Administration.Frontend.Web/';}
<iframe src="https://www.google.com/accounts/Logout" style="visibility: hidden"">/iframe>
<iframe src="http://localhost/Administration.Frontend.Web/?wa=wsignoutcleanup1.0" style="visibility: hidden"></iframe>
Result: i'm logged out from my application and google.
3) Log to my identity provider, click logout, redirected to same URL on ACS as on previous step but now i get 302 result with redirecting to
https://localhost/IdentityProvider/issue/wsfed?wa=wsignout1.0&wreply=https%3a%2f%2fmysite.accesscontrol.windows.net%2fv2%2fwsfederation%3fredirectUrl%3dhttp%3a%2f%2flocalhost%2fAdministration.Frontend.Web%2f
Result: i'm logged out from my application and my identity provider.
4) try to use google again, sucessfully login by entering credential, but logout if failed. I'm logged out from application but not logged from google. And also i see that i don't get page with iframe but instead ACS again try to redirect me to
https://localhost/IdentityProvider/issue/wsfed?wa=wsignout1.0
(and then back to mysite.accesscontrol.windows.net and finally to my application)
Two main question:
Why calling ACS logout give me iframe page with additional
wa=wsignoutcleanup1.0 for google/live but 302 redirect to my
identity provider, may be i miss something in
FederationMetadata.xml?
It looks like ACS after step 3 don't
understand that i successfully logged out from my identity provider
and from this moment try to do it again and again, how to tell them
to stop it?
Here is what you have to do.
First of all, when working with federated authentication always use HTTPS! Sometime protocol negotiations will fail just because it is plain HTTP. Sometimes browsers will block non-secure traffic, which is crucial for the sign-out process. So, always use HTTPS!
Now, to implement the form of single sign out you want you have do some more work.
Your URL for sign-out:
mysite.accesscontrol.windows.net/v2/wsfederation?wa=wsignout1.0&wreply=http%3a%2f%2flocalhost%2fAdministration.Frontend.Web%2f&wtrealm=http%3a%2f%2flocalhost%2fAdministration.Frontend.Web%2f
Do not use it as a parameter to construct SignOutRequestMessage. Use it to directly return Redirect(signOutUrl)!
You have to implement sign-out in two major places!
First place is your general logOff action method (given you are using MVC) Something similar to what you already have but with an important change:
FormsAuthentication.SignOut();
var signoutProtocolLocation = "https://[your_acs_namespace].accesscontrol.windows.net:443/v2/wsfederation?wa=wsignout1.0&wtrealm=[realm]&wreply=[reply]";
FederatedAuthentication.WSFederationAuthenticationModule.SignOut(signoutProtocolLocation);
Note that here I use the overload with string paramer` to redirect result to ACS SSO location!
Now that very ACS SSO location will generate the above mentioned HTML page with JS and couple of iframe elements. One of them will be something like:
<iframe src="http://localhost/Administration.Frontend.Web/?wa=wsignoutcleanup1.0" style="visibility: hidden"></iframe>
Now that particular location http://localhost/Administration.Frontend.Web/?wa=wsignoutcleanup1.0 is the second place in your code where you have implement the SSO. This request must not redirect to a login page, but must instead process correctly and return 200 or 301 response (which in turn will return 200!)! For the sake of simplicity I will only paste the code used here:
if(Request.QueryString.AllKeys.Contains("wa")
&& Request.QueryString["wa"].Equals("wsignoutcleanup1.0"))
{
FederatedAuthentication.WSFederationAuthenticationModule.SignOut(true);
return RedirectToAction("Index");
}
It is really important that you only call the SignOut(true) overload with true when it is request for wsignoutcleanup action. And not when you do general log-off of users.
Please try all mentioned changes and let me know if it solves your issue!