I am using the graph api to reset the users password, but there is a delay when I try the new password, between 1-2 minutes before the new password works. I am using the following endpoint:
PATCH https://graph.microsoft.com/v1.0/users/{{userId}}
{
"passwordProfile" : {
"password": "111111",
"forceChangePasswordNextSignIn": false
}
}
Is there a way to tell when the new password is ready to use or another more efficient endpoint?
I tried to reproduce the same in my environment and got the below results:
I tried to reset password the Azure AD B2C user's password:
PATCH https://graph.microsoft.com/v1.0/users/UserId
{
"passwordProfile" : {
"password": "password",
"forceChangePasswordNextSignIn": false
}
}
After resetting the password even, I faced a delay of a minute before the new password worked.
Note that: By default, there is a replication delay of seconds/minute after resetting the password.
There is a primary in Azure AD which handles the change password(write) and many secondary instances which handles login (read) and so there is a delay after resetting the password to secondary instances.
In order to sign in and change the password, the user must wait at least two minutes.
There is no other endpoint available to show when the new password is ready to use.
graph takes up to 5 minutes in updating/showing the value. What I have used in the past to resolve this is Redis Cache.
When saving the new data (password in your case), save it also in redis for 5 minutes, then when checking, go first to redis, if the value is not there, go to graph.
Related
We are using azure/msal-angular v2.3 with ADB2C Custom policies having MFA enabled with either Phone or Email method.
Many LIVE users are reporting issues while logging in.
At times they need to do MFA twice (at times thrice) to get into the application.
After digging into the audit logs from b2c and HAR file from the customer we observed below error is being raised in User Journey.
{
"error": "invalid_grant",
"error_description": "AADB2C90080: The provided grant has expired. Please re-authenticate and try again. Current time: 1676016884, Grant issued time: 1675941825, Grant expiration time: 1675951412\r\nCorrelation ID: 6052f247\r\nTimestamp: 2023-02-10 08:14:44Z\r\n"
}
As per my understanding msal 2.x automatically handles the token refresh and we don’t need to implement any code for acquiring tokens silently. Don't know why it is expring.
Is it affecting if user keeps the screen idle for long?
Any help to resolve this is appreciated , Thanks in advance.
You are correct that msal 2.x automatically takes care to refresh token. But in few cases when user is inactive for long time or when access token expiry is less than the refresh token default time set.
In that case, acquireTokenSilent() method can be used to obtain a new token.To obtain a new access token silently, call the acquireTokenSilent() method of the MsalService with the desired scopes.
Code:
getToken() {
this.authService.acquireTokenSilent({
scopes: ['<your-scope>'],
}).then((accessToken: string) => {
// Use the access token // console.log('New token:', token);
}).catch((error: any) => {
console.log(error);
});
Check expiration times of the access and refresh tokens, and refresh interval set. Adjust these settings using accessTokenExpirationOffsetInSeconds property to tell number of seconds/minutes before which refresh token has to be set.
this.authService.init({
auth: {
clientId: '<your-client-id>',
authority: '<your-b2c-tenant>.b2clogin.com/<b2c-tenant>.onmicrosoft.com/B2C_1_<policy-name>',
redirectUri: '<your-redirect-uri>',
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true,
accessTokenExpirationOffsetInSeconds: 300,
},
});
}
In this MSAL will try to refresh the access token within 5 minutes ~ 300 seconds before it expires.
This error or failure can happen if the user takes too long to complete the MFA flow.
For that ,In custom policy set the timeout for mutifactor authentication to 5 minutes to give minimum time for user to authenticate.
<OrchestrationStep Order="2" Type="MultiFactorAuthentication">
<MultiFactorAuthentication>
<AuthenticationMethods>
<AuthenticationMethod ReferenceId="phoneFactor" />
<AuthenticationMethod ReferenceId="emailFactor" />
</AuthenticationMethods>
<FailureModes>Deny</FailureModes>
<Default>false</Default>
<SendErrorCodesToResponse>false</SendErrorCodesToResponse>
<Enrollment>Conditional</Enrollment>
<phoneFactor-Timeout>300</phoneFactor-Timeout>
<emailFactor-Timeout>300</emailFactor-Timeout>
</MultiFactorAuthentication>
By theses token refresh behavior can be contolled.
Reference :
Configure authentication in a sample single-page application by using Azure Active Directory B2C | Microsoft Learn
Configure session behavior - Azure Active Directory B2C | Microsoft Learn
I have a WebAPI creating local account in an AzureAD B2C tenant through the AzureAD Graph API. When users are created, they receive an invitation email with a temporary password. User is created in the Graph API with a password profile to force them to change their temporary password on first login.
user.PasswordProfile = new PasswordProfile();
user.PasswordProfile.Password = GetTemporaryPassword();
user.PasswordProfile.ForceChangePasswordNextLogin = true;
When the user login for the first time (through a B2C SignIn policy), it is effectively prompted to change its password and everything is working fine up to that point.
Once the user is logged in, if he signs out then tries to sign in right after, authentification always fails with error message We don't recognize this user ID or password. Please try again. Forgot your password?.
If he uses its previous temporary password then it looks like authentication succeed, but he is asked to change its password again. In that view, Current password does not match the original temporary password but the latest password instead.
I confirmed from the Graph API than prior the first login, the PasswordProfile is still attached to the user.
{
"odata.type": "Microsoft.DirectoryServices.User",
"objectType": "User",
"objectId": "00000000-0000-0000-0000-000000000000",
...
"passwordProfile": {
"password": null,
"forceChangePasswordNextLogin": true,
"enforceChangePasswordPolicy": false
},
...
}
Then after the initial password change, the PasswordProfile to force password reset is no longer here.
{
"odata.type": "Microsoft.DirectoryServices.User",
"objectType": "User",
"objectId": "00000000-0000-0000-0000-000000000000",
....
"passwordPolicies": null,
"passwordProfile": null,
....
}
At that point, the only solution for the user to be able to sign in is to wait for some time (5-10 minutes) prior to be able to login with its latest password.
Any idea about what can be the cause of this delay?
More importantly, How to avoid this delay and the poor user experience associated to it?
It is written in the docs and explicitly explained:
"passwordProfile": {
"password": "P#ssword!",
"forceChangePasswordNextLogin": false // always set to false
},
"passwordPolicies": "DisablePasswordExpiration"
So, as described in the docs, always set the forceChangePasswordNextLogin to false! Also, when using B2C always set the passwordPolicies to DisablePasswordExpiration.
In your provided code sample you make 2 (two) wrong things:
You force password change the next login via
user.PasswordProfile.ForceChangePasswordNextLogin = true;
You do not explicitly disable password expiration.
When using Azure AD B2C it is very important to read the docs first, before taking actions. It is different then a normal Azure AD. And everything is explained in the docs. If you see some missing or wrongful information, please use the feedback form at the bottom of each documentation page.
A feature request to enforce password reset/change upon next login is already logged here. You can give your vote to make it higher in priority list.
As last resort, if you really want to implement this, it might be possible using custom policies (REST API to implement logic to check if the user should change his password).
I want to get email and password after user signup in my Xamarin app, the question is can I get that email and password using this result and then save it to my SQL database in Azure.
async void SignUp() {
DisplayAlert("Result", "Cek Result ", "Ok");
try {
var result = await App.AuthenticationClient.AcquireTokenAsync(
Constants.Scopes,
string.Empty,
UiOptions.SelectAccount,
string.Empty,
null,
Constants.Authority,
Constants.SignUpPolicy);
} catch (MsalException) {
// Do nothing - ErrorCode will be displayed in OnLoginButtonClicked
}
}
and I tried using AuthenticationResult to get user email and password, but I'm only can get user display name , can this AuthenticationResult get user email and password? Or is there any method that can do the job?
An application cannot get the password for security reasons. In the B2C Admin portal, you can select "emails" to be sent to the application in your policy. The email address used for signup will be in this claim when the token is issued.
You could create a user using AD Graph API and write the password in two places. But, if you are storing the password in any other store (e.g. SQL DB), you will also have to figure out how to keep them in sync when the user changes the password.
In addition, is the aspect of how to properly secure the passwords in the DB. One of the value propositions of a directory is to provide such a security layer.
If you update your question with the scenario you are trying to solve, may be there's a workaround that we could figure out.
I have a requirement to eliminate multiple active sessions from being allowed on our site.
It is my understanding that to do this you can manipulate the validateInterval parameter of the OnValidateIdentity property of the CookieAuthenticationProvider as below:
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(0), //Changed from default of 30 minutes
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
I changed the default value from 30 minutes to 0 for testing and it works as anticipated. If I log in to a second browser the next action taken in the first browser redirects me to the login page.
I also allow users to change their password whenever they want (after login). With the validateInterval property at zero, the user is logged out immediately after submitting a password change. They then log back in with the new password and are able to use the site as normal.
If I change the validateInterval parameter value to say 10 seconds, the user is allowed to continue the current session after submitting a password change for 10 seconds and then is redirected to the login page.
Inside the ChangePassword action of the ManageController class the default code that runs after a successful password change is this:
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
}
I thought that the line SignInManager.SignInAsync would keep the user's session going even through a password change (from Logout User From all Browser When Password is changed), but it seems to be controlled additionally by the validateInterval parameter.
If I wanted to allow a user to change their password during an authenticated session without forcing then to login again, could I do this with ASP.NET Identity and still control multiple active sessions? Is there a better way to control multiple active sessions without changing the validateInterval parameter (from Prevent multiple logins)?
Thank you for your help. To clarify, if this behavior is by design, I am fine with it. I just want to understand what is going on so I can defend the behavior to my boss if needed.
Edit:
I failed to mention that I also update the security stamp directly prior to the sign in via SignInManager in the Login action.
Doing what you're doing does not prevent multiple active sessions. I'm also assuming by "sessions" you're talking about multiple authentications by the same user account. Multiple active sessions, in the truest sense, is an entirely different discussion. That said, the cookie that's set to maintain the user's "authenticated" state is client-specific. If I log on from my desktop computer and from my mobile device, or even from both Chrome and Internet Explorer on the same computer, those are all different cookies, unaffected by other cookies that may have been set on other devices or browsers.
The only way you could truly prevent this is to somehow mark the user as "logged in" server-side (i.e. a column on your user table for example). Then, before authenticating them anywhere else (basically in your sign in post action), you would check their user account for this flag. If it's already set, then you would refuse to log them in again until they first log out on the original device/browser. Obviously, your log out action would have to then unset this flag, so they would be allowed to log in again elsewhere.
I am building an app using Loopback API that will be consumed by an iPhone APP. There is a connection to a MySQL database where I run some queries to get some results and expose on the API.
The endpoints contain an ACL that will allow only authenticated users to perform any operation, including the GET ones. So basically the requests need to be done using the ?access_token query string.
I want to set a token that can be saved on the MySQL database and can be used "forever" on the API.
I am not sure if I am asking the right question but if this is not the way to solve this problem, what would it be?
My main point is that I need the requests to be authenticated with a token but I don't want to lose this token once it's set.
Any help is appreciated. Thanks!
By default the max token ttl is 1 year. Thankfully Loopback has an option that will allow you to create a permanent access token:
allowEternalTokens Boolean Allow access tokens that never expire.
https://loopback.io/doc/en/lb3/Model-definition-JSON-file.html#advanced-options
If you're using the default user model, you can enable it in server/model-config.json:
"User": {
"dataSource": "db",
"options": {
"validateUpsert": true,
"allowEternalTokens": true
}
},
Then when logging in, set ttl to -1.
Note that every time you log in (User.login) your token will be replaced with a new one. So if you want to reuse the same access token, log in only once. You can get the existing access token from the AccessToken model (or directly from the database).
If you have a custom user model, you can set allowEternalTokens directly in the model definition file. In addition, if you have a custom user model you'll also need to update the relations of the AccessToken model (either the built-in one or your custom one if you have it) to point to the custom user model.
More info on custom user/access token models here: http://loopback.io/doc/en/lb3/Authentication-authorization-and-permissions.html#preparing-access-control-models
You can pass the ttl in the credential json sent by you iOS app, in this example the token will live for 60sec, just use a high value for make a token "permanent":
POST /Users/login
{
"email":"user#email.com",
"password":"12345689",
"ttl": 60000
}
Or create a before remote method to change the ttl propertie, check this article:
LINK
I set the TTL to the max 1 year but I set the created field to some time very far in the future such as 2112-10-29 00:00:00-04. This makes the token expire a century from now.