Sign up event from Azure B2C - azure

How would I know that a person on my website has just completed the 'Sign Up' process in Azure B2C? Would I have to store my own list of object ids and check against it? I get the feeling I am going to have to do that in any case...

You receive the 'newUser' boolean claim if you have this selected in the signup policy. This will only be sent once, so you need to act on this.

Yes you need to store a list of object id's & verify accordingly in your business logic.

Managed to solve this...
Step 1 (Azure Portal)
In Azure Portal navigate to ADB2C, click on your Sign up and sign in policy
Click on Application claims and make sure to Tick the "User is new" claim
The "User is new" claim will be sent only if the user has just signed-up
Step 2 (Code)
//Using OpenIdConnectOptions
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
OnRemoteFailure = OnRemoteFailure,
OnAuthorizationCodeReceived = OnAuthorizationCodeReceived,
OnAuthenticationFailed = OnAuthenticationFailed,
OnMessageReceived = OnMessageReceived,
OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut,
OnRemoteSignOut = OnRemoteSignOut,
OnSignedOutCallbackRedirect = OnSignedOutCallbackRedirect,
OnTicketReceived = _onTicketReceivedInternal,
OnTokenResponseReceived = OnTokenResponseReceived,
OnTokenValidated = OnTokenValidated,
OnUserInformationReceived = OnUserInformationReceived
};
Notice the _onTicketReceivedInternal Task...
private Task _onTicketReceivedInternal(TicketReceivedContext context)
{
this.OnTicketReceived(context);
//Check if new user
Claim newUserClaim = context.Principal.Claims.ToList().FirstOrDefault(x => x.Type == "newUser");
bool newUser = newUserClaim == null ? false : true;
//Trigger event
if (newUser)
this.OnSignUp(context);
return Task.FromResult(0);
}
//Custom method OnSignUp where an application can do something on user sign up
protected virtual Task OnSignUp(TicketReceivedContext context)
{
return Task.FromResult(0);
}

Related

How do I create a user in code, add it Azure AD B2C, then log in as that user using Azure AD B2C OAuth (MSAL)

Having read the documentation here I thought I should be able to add a user to active directory B2C and then be able to log in as that user. The error message is: "We can't seem to find your account"
[TestMethod]
public async Task CreateUserTest()
{
string mailNickname = Guid.NewGuid().ToString();
string upn = mailNickname + "#mydomain.onmicrosoft.com";
string email = "zzz#gmail.com";
User record = new User { Email = email, DisplayName = "Bob Smith", MailNickname = mailNickname, UserPrincipalName = upn };
record.Identities = new List<ObjectIdentity>();
record.PasswordProfile = new PasswordProfile();
record.Identities.Append(new ObjectIdentity { Issuer = "mydomain.onmicrosoft.com", IssuerAssignedId = email, ODataType = "microsoft.graph.objectidentity", SignInType = "emailAddress" });
record.Identities.Append(new ObjectIdentity { Issuer = "mydomain.onmicrosoft.com", IssuerAssignedId = upn, ODataType = "microsoft.graph.objectidentity", SignInType = "userPrincipalName" });
record.PasswordProfile.Password = "Abcdefgh123!!";
record.AccountEnabled = true;
record.PasswordProfile.ForceChangePasswordNextSignIn = false;
User user = await graphService.CreateUser(record);
Assert.IsNotNull(user);
}
public async Task<User> CreateUser(User user)
{
var result = await client.Users.Request().AddAsync(user);
return user;
}
This login code works if the user logs in using an existing account or creates a new one using the Sign up now link:
export const SignIn = async (appState: AppState): Promise<string> => {
var msg: string = '';
try {
const response = await MSAL.login('loginPopup');
Edit: Add screen cap showing user type and source:
I tried to create a consumer user with code like yours:
And tested with this account in user flow, it returned the token well:
Please check the accounts that you created in your code, the User type always need to be Member and have the Source Azure Active Directory.

How to update Claim.Principal after login a user with Azure B2C

I currently have a project that user B2C Azure for login. In azure I use 3 customs int fields that contains the result of a selection in some dropdownList. after the login occurs in my HOME Controller, I can read the customfield value in my claimsPrincipal like this:
System.Security.Claims.Claim claim = ClaimsPrincipal.Current.Claims.Where(c => c.Type == "extension_RegistrationComplete").SingleOrDefault();
if (claim != null)
retour = Convert.ToBoolean(claim.Value);
So I can test if all custom fields are completed before continuing. If RegistrationComplete are false, I redirect to a Step2 form page asking the user to choose options in dropdownlist. Then I save it to Azure with the B2C Graph API. But the local ClaimsPrincipal do not have the update so the user stuck in a loop because the local RegistrationConplete is always false.
How can we update the ClaimsPrincipal without re-logging the user? Currently I log the user with
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
Thanks.
Finaly I use this code:
var Identity = HttpContext.User.Identity as ClaimsIdentity;
Identity.RemoveClaim(Identity.FindFirst("AnnounceCount"));
Identity.AddClaim(new Claim("AnnounceCount", "Updated Value"));
var authenticationManager =
System.Web.HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new
AuthenticationResponseGrant(new ClaimsPrincipal(Identity), new
AuthenticationProperties() { IsPersistent = true });
I'm not sure what framework you're using, but I've done this in the past by handling SecurityTokenValidated in IAppBuilder.UseWindowsAzureActiveDirectoryBearerAuthentication
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
SecurityTokenValidated = context =>{
//some stuff
Claim newClaim = new Claim("something", "special");
context.AuthenticationTicket.Identity.AddClaim(newClaim);
}
});
}

Not allowed users are authentication in my Azure AD Application Multi Tenant

I have two tenants configured in my Azure AD. My users are authentication with successful in my tenant but others users that are another tenant has access in my applications.
What's wrong with my application? I'm using OpenId Connect protocol in my code, for exemple:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
SecurityTokenValidated = (context) =>
{
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
return Task.FromResult(0);
}
}
});
Am I setting something wrong on my Azure?
Someone help me?
Thanks,
Vilela
I have two tenants configured in my Azure AD.
The tenants are corresponding to the Azure Active Directory. So when there are two tenants that means you have two different Azure Active Directory.( refer here about the detail concept)
And to enable the multi-tenat app, we need to enable it from the old Azure portal and locat you app. Then you can set it by refering the figure below:
Update( limit the sepcifc tenants to access the multi-tenants app)
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ClientId,
Authority = Authority,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
// instead of using the default validation (validating against a single issuer value, as we do in line of business apps),
// we inject our own multitenant validation logic
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// we use this notification for injecting our custom logic
SecurityTokenValidated = (context) =>
{
// retriever caller data from the incoming principal
string issuer = context.AuthenticationTicket.Identity.FindFirst("iss").Value;
var issuer1 = "";
var issuer2 = "";
if ((issuer!=issuer1)&& (issuer != issuer2))
// the caller was neither from a trusted issuer - throw to block the authentication flow
throw new SecurityTokenValidationException();
return Task.FromResult(0);
}
}
});

Azure App Services (Mobile Apps) AAD authentication token refresh

I am trying to use Azure Active Directory to perform login functions on my uwp app. This happens successfully however I cannot get it to refresh the token when it expires and always receive the error "Refresh failed with a 403 Forbidden error. The refresh token was revoked or expired." and so I have to bring up the login window again. I am using the version 2.1.0 and the following code to authenticate:
private async Task<bool> AuthenticateAsync(bool forceRelogon = false)
{
//string message;
bool success = false;
// Use the PasswordVault to securely store and access credentials.
PasswordVault vault = new PasswordVault();
PasswordCredential credential = null;
//Set the Auth provider
MobileServiceAuthenticationProvider provider = MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory;
MobileServiceUser user = null;
try
{
// Try to get an existing credential from the vault.
var credentials = vault.FindAllByResource(provider.ToString());
credential = credentials.FirstOrDefault();
}
catch (Exception ex)
{
// When there is no matching resource an error occurs, which we ignore.
Debug.WriteLine(ex);
}
if (credential != null && !forceRelogon)
{
// Create a user from the stored credentials.
user = new MobileServiceUser(credential.UserName);
credential.RetrievePassword();
user.MobileServiceAuthenticationToken = credential.Password;
// Set the user from the stored credentials.
App.MobileService.CurrentUser = user;
//message = string.Format($"Cached credentials for user - {user.UserId}");
// Consider adding a check to determine if the token is
// expired, as shown in this post: http://aka.ms/jww5vp.
if (RedemptionApp.ExtensionMethods.TokenExtension.IsTokenExpired(App.MobileService))
{
try
{
await App.MobileService.RefreshUserAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
success = true;
}
else
{
try
{
// Login with the identity provider.
user = await App.MobileService
.LoginAsync(provider);
// Create and store the user credentials.
if (credential != null)
vault.Remove(credential);
credential = new PasswordCredential(provider.ToString(),
user.UserId, user.MobileServiceAuthenticationToken);
vault.Add(credential);
success = true;
//message = string.Format($"You are now logged in - {user.UserId}");
}
catch (MobileServiceInvalidOperationException)
{
//message = "You must log in. Login Required";
}
}
//var dialog = new MessageDialog(message);
//dialog.Commands.Add(new UICommand("OK"));
//await dialog.ShowAsync();
return success;
}
Can anyone see something wrong with what I am doing, or need to do anything within the AAD service provider?
You might be able to get more accurate information by taking a look at the server-side application logs. Token refresh failure details will be logged there automatically. More details on application logs can be found here: https://azure.microsoft.com/en-us/documentation/articles/web-sites-enable-diagnostic-log/. I recommend setting the trace level to Informational or Verbose.
Also, if you haven't done this already, Azure AD requires a bit of extra configuration to enable refresh tokens. Specifically, you need to configure a "client secret" and enable the OpenID Connect hybrid flow. More details can be found in this blog post: https://cgillum.tech/2016/03/07/app-service-token-store/ (scroll down to the Refreshing Tokens section and see where it describes the process for AAD).
Besides what has been said about mobile app configuration, I can spot this.
You have:
// Login with the identity provider.
user = await App.MobileService.LoginAsync(provider);
It should be:
user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
new Dictionary<string, string>() {{ "response_type", "code id_token" }});
Maybe this will help:
https://azure.microsoft.com/en-us/blog/mobile-apps-easy-authentication-refresh-token-support/

Using the MVC Authorize attribute with roles using Azure Active Directory + OWIN

I'm building a multi-tenant MVC5 app that follows very closely the sample guidance: https://github.com/AzureADSamples/WebApp-MultiTenant-OpenIdConnect-DotNet/
I'm authenticating against Azure Active Directory and have my own role names that I inject as a Role claim during the SecurityTokenvalidated event:
SecurityTokenValidated = (context) =>
{
// retriever caller data from the incoming principal
string upn = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Name).Value;
string tenantId = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var databaseConnectionString = RoleEnvironment.GetConfigurationSettingValue("DatabaseConnectionString");
AppAnalyzerUser appAnalyzerUser = null;
using (CloudContext dbContext = new CloudContext(databaseConnectionString))
{
if (dbContext.Office365Accounts.FirstOrDefault(x => x.AzureTokenId == tenantId) == null)
throw new GeneralErrorException("Account not found", "The domain that you used to authenticate has not registered.");
appAnalyzerUser = (from au in dbContext.AppAnalyzerUsers
.Include(x => x.Roles)
where au.UserPrincipalName == upn && au.AzureTokenId == tenantId
select au).FirstOrDefault();
if (appAnalyzerUser == null)
throw new AccountNotFoundException();
}
foreach (var role in appAnalyzerUser.Roles)
{
Claim roleClaim = new Claim(ClaimTypes.Role, role.RoleName);
context.AuthenticationTicket.Identity.AddClaim(roleClaim);
}
return Task.FromResult(0);
},
I've decorated some methods with the Authorize attribute like this:
[Authorize(Roles = "SystemAdministrator"), HttpGet]
public ActionResult Index()
{
return View();
}
and the authorize attribute correctly detects that a user is not in that role and sends them back to Azure to authenticate.
However what I see is that the user is already authenticated against Azure AD and is logged in to the app. They don't get the chance to choose a new user account on the Azure screen to log in. So when it bounces them back to Azure AD, Azure AD says "you're already logged in" and sends them right back to the app. The SecurityTokenValidated event fires repeatedly, over and over.
But the user still doesn't have the role required for the method, so they get bounced back to Azure for authentication, and obviously we get stuck in a loop.
Other than writing my own implementation of the Authorize attribute, is there some other approach to solve this problem?
Unfortunately you stumbled on a known issue of [Authorize]. For a description and possible solutions see https://github.com/aspnet/Mvc/issues/634 - at this point writing a custom attribute is probably the most streamlined workaround.

Resources