We are using ADAL.Net to create users in Azure AD. using SinginNames, we can provide any email address (gmail or non-domain emails) as username to create Azure AD Local account.
when we try the same using Microsoft Graph (MSAL.Net), we are not able to create a user:
Code: Request_BadRequest
Message: Property userPrincipalName is invalid.
How can we create gmail address as the username using Microsoft Graph or the Microsoft Graph Client Library?
The newly created account should be local user account, not a guest user.
var user = new User
{
AccountEnabled = true,
DisplayName = "displayName-value",
MailNickname = "mailNickname-value",
UserPrincipalName = "vetrivelmp1#gmail.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "Test123!##"
}
};
var graphClient = await _msGraphHelper.GetMsGraphClientAsync();
var createdUser = await graphClient
.Users
.Request()
.AddAsync(user);
No You can not. You must have to request with tenant specific email following sample:
Request URL: https://graph.microsoft.com/v1.0/users
{
"accountEnabled": true,
"displayName": "KironTestDisplayName",
"mailNickname": "KironTestNickName",
"userPrincipalName": "KironTestingCreateUserWithMember#MyTenant.onmicrosoft.com",
"userType":"guest",
"passwordProfile" : {
"forceChangePasswordNextSignIn": true,
"password": "Test#pass420"
}
}
Points to Remember:
Get the proper token in which tenant you are going to create user
Need necessary request permission
userPrincipalName should follow as
UserName#tenant-value.onmicrosoft.com
"userType":"guest" Or "Member" You can add
Note: Mail should be like myUser#Mytenant.onmicrosoft.com Other then you would encounter 400 request error like Property userPrincipalName is invalid
Your Case:
If you wanted to create user using gmail account then request pattern need to be changed You have to request for invitation API like below:
Request Url: https://graph.microsoft.com/v1.0/invitations
Request Body:
{
"invitedUserEmailAddress": "TestGmailUser#gmailUser",
"inviteRedirectUrl": "https://myapp.com"
}
Response:
Note: If go to azure portal you would see you cannot add gmail user as domain member you add as guest user after invitation. So this
why you need above request pattern. Hope you are clear now.
Gmail User Add SDK:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var invitation = new Invitation
{
InvitedUserEmailAddress = "TestGmailUser#gmailUser",
InviteRedirectUrl = "https://myapp.com"
};
await graphClient.Invitations
.Request()
.AddAsync(invitation);
Azure Portal Verify:
I have successfully added gmail user on my portal using above request. See the screen shot below:
For those still trying to accomplish this, the accepted answer here wasn't helpful, or at least is not the case here in 2021. I was able to create local B2C accounts with personal email addresses as well as custom sign in names using Example 2: Create a user with social and local account identities.
POST https://graph.microsoft.com/v1.0/users
Content-type: application/json
{
"displayName": "John Smith",
"identities": [
{
"signInType": "userName",
"issuer": "contoso.onmicrosoft.com",
"issuerAssignedId": "johnsmith"
},
{
"signInType": "emailAddress",
"issuer": "contoso.onmicrosoft.com",
"issuerAssignedId": "jsmith#yahoo.com"
},
{
"signInType": "federated",
"issuer": "facebook.com",
"issuerAssignedId": "5eecb0cd"
}
],
"passwordProfile" : {
"password": "password-value",
"forceChangePasswordNextSignIn": false
},
"passwordPolicies": "DisablePasswordExpiration"
}
Related
I’m working on a project where I need access to a users mailbox (similar to how the MS Flow mailbox connector works), this is fine for when the user is on the site as I can access their mailbox from the graph and the correct permissions request. The problem I have is I need a web job to continually monitor that users mail folder after they’ve given permission. I know that I can use an Application request rather than a delegate request but I doubt my company will sign this off. Is there a way to persistently hold an azure token to access the user information after a user has left the site.. e.g. in a webjob?
Edit
Maybe I've misjudged this, the user authenticates in a web application against an Azure Application for the requested scope
let mailApp : PublicClientApplication = new PublicClientApplication(msalAppConfig);
let mailUser = mailApp.getAllAccounts()[0];
let accessTokenRequest = {
scopes : [ "User.Read", "MailboxSettings.Read", "Mail.ReadWrite", "offline_access" ],
account : mailUser,
}
mailApp.acquireTokenPopup(accessTokenRequest).then(accessTokenResponse => {
.....
}
This returns the correct response as authenticated.
I then want to use this users authentication in a Console App / Web Job, which I try to do with
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithClientSecret(Secret)
.WithAuthority(Authority, true)
.WithTenantId(Tenant)
.Build();
System.Threading.Tasks.Task.Run(async () =>
{
IAccount test = await app.GetAccountAsync(AccountId);
}).Wait();
But the GetAccountAsync allways comes back as null?
#juunas was correct that the tokens are refreshed as needed and to use the AcquireTokenOnBehalfOf function. He should be credited with the answer if possible?
With my code, the idToken returned can be used anywhere else to access the resources. Since my backend WebJob is continuous, I can use the the stored token to access the resource and refresh the token on regular intervals before it expires.
Angalar App:
let mailApp : PublicClientApplication = new PublicClientApplication(msalAppConfig);
let mailUser = mailApp.getAllAccounts()[0];
let accessTokenRequest = {
scopes : [ "User.Read", "MailboxSettings.Read", "Mail.ReadWrite", "offline_access" ],
account : mailUser,
}
mailApp.acquireTokenPopup(accessTokenRequest).then(accessTokenResponse => {
let token : string = accessTokenResponse.idToken;
}
On the backend, either in an API, webJob or Console:
var app = ConfidentialClientApplicationBuilder.Create(ClientId)
.WithClientSecret(Secret)
.WithAuthority(Authority, true)
.WithTenantId(Tenant)
.Build();
var authProvider = new DelegateAuthenticationProvider(async (request) => {
// Use Microsoft.Identity.Client to retrieve token
List<string> scopes = new List<string>() { "Mail.ReadWrite", "MailboxSettings.Read", "offline_access", "User.Read" };
var assertion = new UserAssertion(YourPreviouslyStoredToken);
var result = await app.AcquireTokenOnBehalfOf(scopes, assertion).ExecuteAsync();
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
});
var graphClient = new GraphServiceClient(authProvider);
var users = graphClient.Me.MailFolders.Request().GetAsync().GetAwaiter().GetResult();
In the end I had to abandon using the ConfidentialClientApplicationBuilder, I still use PublicClientApplicationBuilder on the front end to get the users consent but then I handle everything else with the oauth2/v2.0/token rest services which returns and accepts refresh tokens.
That way I can ask the user for mailbox consent using PublicClientApplicationBuilder
Access the user mailbox at any time using oauth2/v2.0/token
We're trying to create a user in Azure AD B2C (cloud tenant, not local) via the C# library. I cannot log in with the new user; it's saying the password has expired. From the AD B2C Sign-in log:
However, I'm creating them with password expiration disabled like so:
PasswordProfile passwordProfile = GetPasswordProfile(password);
var newB2cUser = new User
{
Id = id,
MailNickname = id,
UserPrincipalName = $"{id}#{tenant}",
GivenName = claims.FirstName,
Surname = claims.LastName,
DisplayName = displayName,
AccountEnabled = true,
PasswordProfile = passwordProfile,
PasswordPolicies = "DisablePasswordExpiration,DisableStrongPassword",
Identities = new List<ObjectIdentity>
{
new ObjectIdentity
{
SignInType = "emailAddress",
IssuerAssignedId = claims.Email,
Issuer = tenant
}
}
};
User createdB2cUser = await this.GraphServiceClient.Users.Request().AddAsync(newB2cUser);
...and with the password profile created like this:
private static PasswordProfile GetPasswordProfile(string password)
{
var passwordProfile = new PasswordProfile
{
Password = password,
ForceChangePasswordNextSignIn = false,
ForceChangePasswordNextSignInWithMfa = false
};
return passwordProfile;
}
We've used code like the above to create users all the time that need to reset their password on login. Now we need to create a user with a preset password that won't immediately expire (e.g., seeding a test environment). I added what I thought were the correct password policies and password profile above. What's going on?
I can see nothing wrong with the code sample you provided. But did you try achieving the same goal using Graph API directly? Just to understand what is going on and where the fault may be. You can use GraphExplorer: https://developer.microsoft.com/en-us/graph/graph-explorer
Invoke a POST request to the https://graph.microsoft.com/v1.0/users endpoint with the similar JSON to see whether you face the same issue.
{
"id": "fcaba916-17b0-4d88-84ea-03371d55067a",
"mailNickname": "MelissaD",
"userPrincipalName": "MelissaD#contoso.onmicrosoft.com",
"givenName": "Melissa",
"surname": "Darrow",
"displayName": "Melissa Darrow",
"accountEnabled": true,
"passwordPolicies": "DisablePasswordExpiration,DisableStrongPassword",
"passwordProfile": {
"password": "da413e48-180d-1945-6025-d7e5947711ed",
"forceChangePasswordNextSignIn": false,
"forceChangePasswordNextSignInWithMfa": false
},
"identities": [
{
"signInType": "emailAddress",
"issuer": "yourissuer",
"issuerAssignedId": "email"
}
]
}
Try to simulate the request body exactly as Graph Client does to know what is going on behind the scenes.
I need to get emails from my Office365 account programmatically (C#).
I decided to use Mailkit and to create an application password on Azure portal.
I registered a new app, set its Redirect Uri and gave it some permissions:
I then created a client secret to access the account without user interaction.
Now, here is my code:
var opt = new ConfidentialClientApplicationOptions()
{
ClientId = "xxx_clientid",
TenantId = "xx_tenant_id",
ClientSecret = "xxx_client_secret_value",
RedirectUri = "http://localhost",
};
var scopes = new string[] {
"email",
"offline_access",
"https://outlook.office.com/IMAP.AccessAsUser.All", // Only needed for IMAP
//"https://outlook.office.com/POP.AccessAsUser.All", // Only needed for POP
//"https://outlook.office.com/SMTP.Send", // Only needed for SMTP
};
var app = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(opt).Build();
var authToken = await app.AcquireTokenForClient(scopes).ExecuteAsync(); // <--- Exception
var oauth2 = new SaslMechanismOAuth2(authToken.Account.Username, authToken.AccessToken);
using (var client = new ImapClient(new ProtocolLogger("imapLog.txt")))
{
client.Connect("outlook.office365.com", 993, SecureSocketOptions.SslOnConnect);
//client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Authenticate(oauth2);
var inbox = client.Inbox;
inbox.Open(MailKit.FolderAccess.ReadOnly);
Console.WriteLine("Total messages: {0}", inbox.Count);
Console.WriteLine("Recent messages: {0}", inbox.Recent);
client.Disconnect(true);
}
Running the code I get this exception:
Microsoft.Identity.Client.MsalServiceException: 'AADSTS70011: The provided request must include a 'scope' input parameter. The provided value for the input parameter 'scope' is not valid. The scope email offline_access https://outlook.office.com/IMAP.AccessAsUser.All is not valid.
I tried following the guide found on GitHub:
Using OAuth2 With Exchange
The problem is that I need to use an app password instead.
I want to update user profile (Federated_status) of user, I am using DocuSign admin APi and generated temporary token from organization admin account - but when I call the Docusing admin api using below it doesn't update it and throws error "unauthorized".
'''try
{
HttpContent PostContent = new StringContent(JsonConvert.SerializeObject(ObjUser), Encoding.UTF8, "application/json");
string reponsebody = string.Empty;
string Url = "https://api-d.docusign.net/managment/v2/organisation/" + OrgID + "users/profiles";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application / json"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token));
HttpResponseMessage rep = client.PostAsync(new System.Uri(Url), PostContent).Result;
reponsebody = rep.Content.ReadAsStringAsync().Result;
}
var userProfileResponse = JsonConvert.DeserializeObject<UserProfile>(reponsebody);
return userProfileResponse;
}
'''
I am not sure how to call this admin api to update user profile - "federated status" .
Please help.!
You can do this:
GET /v2/organizations/{organizationId}/users/profile
but the update is:
POST /v2/organizations/{organizationId}/users/profiles
Different endpoint, I know it's confusing.
Make sure you do a POST, not a GET
https://developers.docusign.com/orgadmin-api/reference/Users/Users/updateUser
I suspect that you are not including the proper scopes when obtaining your access token. For requests to DocuSign Admin API, you must include special scopes. I've included some code examples for each type of authentication.
If you're using JWT, your token body will look something like this...
{
"iss": "5c2b8d7e-xxxx-xxxx-xxxx-cda8a50dd73f",
"sub": "464f7988-xxxx-xxxx-xxxx-781ee556ab7a",
"iat": 1523900289,
"exp": 1523903289,
"aud": "account-d.docusign.com",
"scope": "signature organization_read group_read user_read user_write"
}
For Auth Code Grant, your request must include the DocuSign Admin scopes, included in this sample:
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20organization_read%20group_read%20user_read%20user_write&client_id=7c2b8d7e-xxxx-xxxx-xxxx-cda8a50dd73f&state=a39fh23hnf23&redirect_uri=http://example.com/callback/http://example.com/callback/
For Implicit grant, your request will be similar, except a different response type
https://account-d.docusign.com/oauth/auth?response_type=token&scope=signature%20organization_read%20group_read%20user_read%20user_write&client_id=7c2b8d7e-xxxx-xxxx-xxxx-cda8a50dd73f&state=a39fh23hnf23&redirect_uri=http://example.com/callback/
For more information on the various types of scope, see our guide on DocuSign Admin API Auth. Let me know if that helps.
I have a Azure Active Directory B2C tenant. I also have a small service that creates new users in the B2C tenant from a different system. This way I can synchornize both systems. When a user has been added to B2C and logs in the first time, I want the user to be forced to change the password. But whatever I do, the user can just log in and continue, without changing the password..
To add a user to B2C, I use the Microsoft Graph 1.14 package. I push the user information as JSON to the endpoint https://graph.windows.net/{tenantId}/users?api-version=1.6
The log in page is an Azure custom page in the user flow policies. There is also a change password policy, if needed.
This I tried:
When adding the user, I set the password profile. Adding the property "ForceChangePasswordNextLogin" and setting it to true, does not work.
Someone suggested to add the "ForceChangePasswordNextSignIn" property, but B2C doesn't know this property.
Tried to fix it in the policy; didn't work.
Used Google and StackOverflow; not much luck.
This is the user I post to Microsoft Graph:
var user = new GraphUserModel
{
City = "Amsterdam",
CustomField= "999999",
Department = "TestPassword",
DisplayName = "TestPassword",
OtherMails = new[] { "myemail#something.nl" },
PostalCode = "1234 AB",
StreetAddress = "Hoofdweg 6",
Surname = "TestPassword",
TelephoneNumber = "0123456789",
ChainCode = null,
MailNickname = "999999",
UserPrincipalName = "999999#{tenantNameHere}",
SignInNames = new List<SignInNames>
{
new SignInNames
{
Type = "userName",
Value = "999999"
}
},
AccountEnabled = true,
CreationType = "LocalAccount",
PasswordProfile = new PasswordProfile
{
Password = "SomeRandomPassword"
},
PasswordPolicies = "DisablePasswordExpiration"
};
The users are created correctly, but when they log in for the first time, I would like to see a page where they are forced to change the password.
With Sign-up/Sign-in policy I had to implement that manually by flagging users in DB if they have changed the password and then redirecting to password change if they have not changed the password.
I was also not able to find 'out of the box' solution. I found that forceChangePasswordNextLogin works only with Sign-in policy.