SaaS App with AzureADB2C: User Flows for Client SSO? - azure

We have a SaaS web app and our clients are requiring SSO authentication for each of them. We are using AzureADB2C and it works great, but now are looking at adding SSO.
I put in the SSO setup into the B2C tenet and it works great, but really messed up our login screen with a "MyCompanySSO" button to log in with, on our customer-facing login screen.
So now my idea is to have a separate user flow that handles each SSO setup. Starting with us. We'd go to MyCompany.OurSaaSApp.us and that'd forward them directly to the user flow endpoint and prompt them to login with their SSO account (AzureAD).
This all seems to try to work, but I'm getting these errors within the AzureADB2C middleware:
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Warning: .AspNetCore.Correlation. state property not found.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Information: Error from RemoteAuthentication: Correlation failed..
Then I get pumped out onto a error page and the login fails.
So 2 things...
1.) Am I going in the right direction knowing what we're wanting to accomplish
2.) What do we need to do to resolve this?
Thanks everyone for the help, it's been greatly appreciated.
(note:)
Just to reiterate. The SSO works properly when the custom identity provider is attached to the existing SignUpOrIn UserFlow I have configured in the app. I'm only getting this error when I try to use another UserFlow that I want to use specifically for this SSO.

I'm not sure about that specific error, although "state" parameter is a parameter that your app sends in the request that will be returned in the token for correlation purposes.
Using and different policy for each federation sounds like the right approach, but if you are doing from a single instance of your app, you'll need to modify the OIDC protocol message with the correct authority (ie policy) on redirect.
In your OIDC middleware configuration, set up a handler for the RedirectToIdentityProvider notification. Then handle it with something like:
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
//var policy = notification.OwinContext.Get<string>("Policy");
var tenantSegment = notification.Request.Path.Value.Split(new char [] { '/'}, StringSplitOptions.RemoveEmptyEntries)[0];
if (!string.IsNullOrEmpty(tenantSegment) && !tenantSegment.Equals(DefaultPolicy))
{
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), $"B2C_1A_{tenantSegment.ToLower()}_SignUpSignInPolicy");
}
return Task.FromResult(0);
}
If you need to inject anything else tenant-related, that would be the place to do it.

Related

BotBuilder Authentication Multitenant

I want to create Microsoft BotBuilder following this tutorial. But it seems SO complicated compared to v3.(BTW: starting a tutorial with 3 authentications that cover 75% of the article is not a good sign)
So I follow the EchoBot sample (I chose MultiTenant because my server is outside AND it seems the most covered):
const credentialsFactory = new BotBuilder.ConfigurationServiceClientCredentialFactory({
MicrosoftAppId: '***',
MicrosoftAppPassword: '***',
MicrosoftAppType: 'MultiTenant',
});
const botFrameworkAuthentication = BotBuilder.createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory);
const onTurnErrorHandler = async (context, error) => { /* for errors */ }
const adapter = new BotBuilder.CloudAdapter(botFrameworkAuthentication);
adapter.onTurnError = onTurnErrorHandler;
Questions:
How do I test everything is working ? isValidAppId() and isAuthenticationDisabled() are the only available method and seems OK.
How do I get MicrosoftAppPassword ? According to the documentation I have to click manage, then create a value/secret pair. Should I use value ? or secret ? Why none is named password ? Anyway none works ...
To test if it works, I follow the sample:
setup an HTTP POST Endpoint (with Node-RED)
declare the endpoint in Azure Portal Bot Configuration
go to webchat to test
I correctly receive the Messages then try to do some authentication/parisng (I assume) :
await adapter.process(msg.req, msg.res, (context) => {
/* do some stuff */
});
But it fails with a very explicit error :
Error: 1 validation issue(s)
Issue #0: custom_error at [[root]]
Response
I think, the errors is related to an authentication issue, since I don't understand what/how to set the password. I guess I have to go through this CloudAdapter in order to get a parsed context and be able to send messages.
EDIT 07/05/2022:
I use the AppId from here :
I click "Manage" but where is the AppPassword ?
EDIT 12/05/2022:
Using cURL I manage to validate the appID and appPassword (the value (hidden by stars) of the secret).
BotBuilder is mixing the Communication Stack (HTTP / WebSocket) and the Logic stack (Turn Conversation). I think it's a bad habbit but I manage a workaround:
I use a BotFrameworkAdapter instead of CloudAdpater
I call adapter.processActivity() instead of adapter.process()
The adapter still want to end() the request and set deprecated values but it works in Node-RED. The context handle all the requirement to call sendActivity() anywhere multiple times.
To answer second question in your case, kindly go through the link :https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration?view=azure-bot-service-4.0&tabs=multitenant
To answer the first question in your case, kindly check disabling and enabling the authentication to test the app: https://learn.microsoft.com/en-us/azure/bot-service/bot-service-troubleshoot-authentication-problems?view=azure-bot-service-4.0&tabs=csharp
As far as getting the password goes, when you create a new Multi Tenant Azure Bot resource, the app password goes into the Azure Key Vault created alongside it. The AppId and AppPassword are randomly generated by Azure. You can get them from the key vault in the Azure portal in the correct resource group.
If you want to create a resource manually using the CLI and define your own password, you can use this docs page for deploying a bot. Make sure you select the correct tabs. I have pre-selected C# and Multi Tenant in a new resource group for the above link.
Single Tenant is for limiting your bot's connections to Azure resources within the same tenant, and a User Assigned Managed Identity is if you want to make use of an Azure Managed Identity across the bot's resources instead of having a password for each resource.
You should be able to simply add the AppId and AppPassword to the echo bot sample and deploy it.

ADB2C and MSAL with separate signup and signin policies causing issues

I'm trying to use ADB2C custom policies with the MSAL.js library on a static web app
Our policies are reasonably complex, so we've split the 'signup' into it's own flow, but now I'm having trouble with the handover from signup to signin. From what I'm reading it sounds like each IEF policy assigns its own tokens, and so a token generated from a signup flow cannot be used to signin? Even if they're both associated with the same ADB2C tenant?
Is this correct? It seems odd if so, as the keys (behind the discovery document/jwks_uri) are identical on both policies, as is the issuer.
The error that msal reports is during an 'acquireTokenFromNetworkStart' request which returns a 400 Bad Request Silent SSO could not be completed - insufficient information was provided. Please provide either a loginHint or sid.
So perhaps I just need to adjust the session management on the ADB2C policies? Do I need to emit the sid (session id) and then use that with msal when re-acquiring tokens?
Any advice would be most welcome, I cannot find a well documented example that puts all of this together.
Please check if it can be worked around like below .
Try to include your scope in the initial loginRedirect or loginPopup or while calling acquireTokenRedirect or acquireTokenPopup before calling acquireTokenSilent.
Scopes that is created in expose an api like "user.read", "mail.send" in your login request in code and grant consent for the same .
That means we need to Call acquireTokenSilent with your resource scope.
var loginRequest = {
scopes: ["openid","user.read", "mail.send"]
};
try {
msalInstance.loginRedirect(loginRequest);
} catch (err) {
// handle error
}
In B2C ,your tenant will need to be configured to return the emails claim on idTokens .Reference
Also try to include application Id or app id uri in the scopes .
References:
microsoft-authentication-library-for-js/issues
cannot-get-access-token-in-react-app/SO Reference

Azure ADB2C Single Sign Out W/ Azure AD as Identity Provider

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

High Trust S2S Provider Hosted App with "App + User" Policy

I am relatively new to sharepoint app development.
Trying to create a on premises, High Trust provider hosted app with App + User Policy. I have followed below document to create a demo.
https://msdn.microsoft.com/library/office/fp179901(v=office.15)
http://blogs.msdn.com/b/russmax/archive/2014/06/23/part-1-intro-to-provider-hosted-apps-setup-the-infrastructure.aspx
I am facing few issue and I have some question to clarify, if anybody can help.
1) When I inspect my request in dev tools, it give me below form data.
SPAppToken:
SPSiteUrl:
SPSiteTitle:Home
SPSiteLogoUrl:
SPSiteLanguage:en-US
SPSiteCulture:en-US
SPRedirectMessage:EndpointAuthorityMatches
SPErrorCorrelationId:f069e89c-a0cd-20ce-a1c0-7db95db0334b
now when i inspect log with above corelation id, i am finding below errors.
-- Error when get token for app i:0i.t|ms.sp.ext|ab8ff461-bc75-4516-b475-b666ac47eec0#802f23e1-6e11-45d1-909c-07a7b0ab0ce2,
exception: Microsoft.SharePoint.SPException: The Azure Access Control
service is unavailable.
-- App token requested from appredirect.aspx for site: 92bfe5c4-7255-4b09-a89a-07e0e2b03622 but there was an error in
generating it. This may be a case when we do not need a token or when
the app principal was not properly set up.
-- Getting Error Message for Exception Microsoft.SharePoint.SPException: The Azure Access Control service is
unavailable.
a) I belive in high-trust app it shouldn't look for Azure ACS.
Is this error because of some incorrect configuration?
b) SPAppToken is null here. Is it null always in case of hig trust app?
2) Say I am logged into sharepoint with User A and trying to launch sharepoint app.
Within app code I want to get identity of logged in user(which is A). From below code i found that Request.LogonUserIdentity gives me identity of user A. But how can we sure that request is came from sharepoint only. I can copy the same app URL and paste in browser window and login with window credential and get the same result. So question is how can I verify if its legitimate request came from sharepoint only and no one is faking request.
ALos, when I inspect request in dev tools, its passing Authorization key in request header. What is use of this?
using (var clientContext = TokenHelper.GetS2SClientContextWithWindowsIdentity(hostWeb, Request.LogonUserIdentity)) { clientContext.Load(clientContext.Web, web => web.Title); clientContext.ExecuteQuery(); Response.Write(clientContext.Web.Title); }
3) Also what happens if my app doesnt support windows authentication and only support FBA, is there any way to get user identity in this case?
Any help would be much appreciated.
Thanks
For issue #1: It looks to me that the step # 9 (Configure authentication settings) in this section (from the first MSDN article you have referred) was missed, i.e., 'ACS Control service' was selected instead of 'Use a Certificate' option.
For issue #2: There are helper methods in TokenHelper.cs to validate the AccessToken from the HttpRequest, which identifies the validity of the request.

Azure ACS, WIF 3.5, Asp.Net 3.5 Custom Membership Provider and IsAuthenticated

Following the steps in this guide Using Azure ACS I have a working Azure ACS service configured & authenticating via Facebook, redirecting back to a website running on my development server.
On authentication success Azure ACS redirects back to my local development website and the IsAuthenticated flag is true, however I want to set the IsAuthenticated flag to true only if the email from the claim also exists in my local database, via a check/call to a custom MembershipProvider. If the email from the claim does not exist I want to redirect the client to a register page. Once registered and authenticated I would like to set the IsAuthenticated flag to true.
Currently once authenticated with Facebook and AzureACS, a user can request a secure page such as ViewAccountBalance.aspx, even though the account does not exist since out of the box IsAuthenticated flag to true. Interested to hear what others have done and what the best practice is.
You'll need to make a clear difference between authentication and authorization. Since the user logged in through Facebook it means he's authenticated (you know who he is and where he comes from).
Now, if you want to restrict parts of the application based on a specific condition you're actually talking about authorization. You might consider combining roles with a simple HttpModule. Example: your HttpModule could verify which page the user is browsing. If the user accesses a page that requires an active profile, you could use the following code:
public class RequiresProfileHttpModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AuthorizeRequest += new EventHandler(OnAuthorize);
}
private void OnAuthorize(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if (app.Request.Url.ToString().Contains("bla") && !app.Context.User.IsInRole("UsersWithProfile"))
app.Response.Redirect("http://myapp/register.aspx");
}
}
The only thing you'll need to take care of is to update the principal to make sure it has the role UsersWithProfile if the user filled in his email address.
This is just one of many possible solutions. If you're using ASP.NET MVC you could achieve the same result with global ActionFilters. Or, you could also try to work with the IClaimsPrincipal (add a claim if the user has a profile).
Sandrino is correct. You can use role based authorization (or more generally, claim based authorization). By default, ACS simply returns the claims issued by the identity providers to your relying party. For Facebook, it will return an email claim. However, you can configure ACS to create additional rules. For example, you can map particular users to a role whose value is administrator. Then ACS will also return this role claim to your relying party. Then you can use Sandrino’s suggestion to use role based authorization. You can also refer to http://msdn.microsoft.com/en-us/library/windowsazure/gg185915.aspx for more information.

Resources