I'm having trouble in adding custom claim (tenant id) to my token. So my program allows users to switch their workspace using tenant id. In order to do that I need to receive the selected Tenant Id and generate new token with the selected tenant id inside the token.
To generate the token first time during login, I used this code
var tokenResponse = await httpclient.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = discoveryDocument.TokenEndpoint,
UserName = model.PhoneNumber,
Password = model.Password,
ClientId = Configuration["ClientInformation:ClientId"],
ClientSecret = Configuration["ClientInformation:ClientSecret"]
});
And to generate new token after they login to switch the tenant, I used refresh token.
var tokenResponse = await httpclient.RequestRefreshTokenAsync(new RefreshTokenRequest
{
Address = discoveryDocument.TokenEndpoint,
ClientId = Configuration["ClientInformation:ClientId"],
ClientSecret = Configuration["ClientInformation:ClientSecret"],
RefreshToken = model.RefreshToken
});
I already seen how profile service receive data using claimsprincipal and thats where I was confused how to insert new value inside the claimsprincipal
So my question is how do I send the selected tenant id to profile service?
I'm able to do this by saving the tenant id that the user wants to the database and I'm able to get the tenant value using profile service
Related
How to generate Azure Active Directory (AAD) authentication token for Graph API without interactive login screen for console/native application?
Details:
I am using Graph API to read emails with Azure Active Directory (AAD) with ‘’Delegated’’ permissions.
”Application” permission allows a user to read other mailboxes and there is no admin consent for this approach due to security concerns, so I am using ‘’Delegated’’ permissions.
My console/native application is registered to AAD.
Since AAD generates OAuth Authentication token for a specific account using:
1. Client ID
2. Tenant ID
3. Client Secret (Key/password for the application)
4. Login credentials of a specific account.
I can generate a token using an interactive login screen.
However, I want a mechanism where I can generate AAD token for Graph API (resource) without an interactive login screen within code using C# or.NET
Its seems you are trying to get your token without prompting the sign in page.
Yeah, you can do it using client_credentials grant authentication flow within C#.Net
See the following code snippet:
Access Token Class:
public class AccessTokenClass
{
public string access_token { get; set; }
public string token_type { get; set; }
public long expires_in { get; set; }
}
Token Request Method:
private async Task<string> GetYourTokenWithClientCredentialsFlow()
{
string tokenUrl = $"https://login.microsoftonline.com/YourTenant/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "5f14dea0-5cd---Your_Client_Id----8950-4f646829f870",
["client_secret"] = "031Fnwih---Your_Client_Secret----Fx+Ase3V65lpWQ=",
["resource"] = "https://graph.microsoft.com" // https://management.azure.com/ Or Any Resource You Want
});
dynamic json;
dynamic token;
HttpClient client = new HttpClient();
var tokenResponse = await client.SendAsync(tokenRequest);
json = await tokenResponse.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<AccessTokenClass>(json);
Console.WriteLine("Your Access Token {0}",token.access_token);
return token;
}
Generated Token Response:
Once you have set all of your required credentials you would get the token in response. See the screen shot below:
Note: This authentication flow would generate token for you without interactive login screen. If you still have any query feel free to share in comment. Thanks and happy coding!
Update:
To assign dedicated permission for reading mail. Follow the below steps:
Azure active directory
App registration
Select your app
API permissions
Add a permission
Microsoft graph
Delegated permissions
Mail
Mail.Read (read user mail)
Add permission
Grant admin consent
See the screen shot:
It worked for me with the below code. I am able to recieve the token now with the user credentials and can read the mailbox.
private static async Task<string> GetToken()
{
string authority = "https://login.microsoftonline.com/{tenantId}";
string resource = "https://graph.microsoft.com";
string userName = "xxxxxxxxx";
string password = "xxxxxxx";
string clientId = "Your Client ID (GUID)";
UserPasswordCredential userPasswordCredential = new UserPasswordCredential(userName, password);
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
var result = AuthenticationContextIntegratedAuthExtensions.AcquireTokenAsync(authenticationContext, resource, clientId, userPasswordCredential).Result;
return result.AccessToken;
}
Using Microsoft.IdentityModel.Clients.ActiveDirectory(2.22.302111727) I am trying to get the access token and the refresh token via ADAL AcquireToken(resourceUri, new ClientCredential(clientId,clientSecret) but I am able to get the only access token only but when I am using the same app with AcquireToken(resourceUri, clientId, userCredential) and passing the username and password as the userCredentials then I am able to get both the access as well as refresh token.
string resourceUri = "https://graph.microsoft.com";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
AuthenticationResult token = authContext.AcquireToken(resourceUri, new
ClientCredential(clientId,clientSecret));
string resourceUri = "https://graph.microsoft.com";
string clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
AuthenticationResult token = authContext.AcquireToken(resourceUri, new
ClientCredential(clientId,clientSecret));
I have also tried using AcquireToken(clientId, resourceUri, new Uri(redirectUri)) but it gives me error "AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'" and I have tried AcquireToken(resourceUri, clientId, new Uri(redirectUri),
PromptBehavior.RefreshSession ,new UserIdentifier("xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", UserIdentifierType.UniqueId)) and it gives me error "AADSTS650057: Invalid resource. The client has requested access to a resource which is not listed in the requested permissions in the client's application registration. Client app ID: 00000003-0000-0000-c000-000000000000(Microsoft Graph)."
Please explain the exact resource value that is missing here in error AADSTS650057.
You may be hitting several different error. OAuth2 client credentials grant does not return refresh tokens (enter link description here ) as you can get a new token with the existing credentials. Requests involving user credentials do return refresh tokens as you may want to renew the access token without having to prompt the user for credentials.
AADSTS650057 is most likely caused by your application being registered in AAD without having permission to call MS Graph. An application must have either Application Permission (when using Client Credentials) or Delegated Permission (when using user creds) to get a token to this resource.
1.Install this from nuget package
using Microsoft.Identity.Client;
2. add this code
string clientId = "Get it from Admin"
string clientSecret = "Get it from Admin"
string authorityUrl = "Get it from Admin"
string authorityId = "Get it from Admin";
string authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", authorityId);
var app = ConfidentialClientApplicationBuilder.Create(clientId).WithAuthority(authority).WithClientSecret(clientSecret).Build();
var authResult = Task.Run(() => app.AcquireTokenForClient(new[] { $"{apiEndpointUrl}/.default" }).ExecuteAsync()).Result;
return authResult.AccessToken;
I have implemented multi tenant app.Now I would like to restrict some of the groups from other tenant.
For Eg: My tenant aaaaaaa.com. other tenant xxxxxxxxxxxxx.com
So i would like to get "xxxxxxxxxx.COM" user group info and also user info in C#.
I have tried GraphApi for getting other tenant and my tenant user info and group info.but i didn't get solution.i tried like below
// These are the credentials the application will present during authentication
// and were retrieved from the Azure Management Portal.
// *** Don't even try to use these - they have been deleted.
const string clientID = "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx";
const string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// The Azure AD Graph API is the "resource" we're going to request access to.
const string resAzureGraphAPI = "https://graph.windows.net";
// The Azure AD Graph API for my directory is available at this URL.
const string serviceRootURL = "https://graph.windows.net/xxxxxxxxxxxxxxx";
public async Task<User> userExists()
{
Uri serviceRoot = new Uri(serviceRootURL);
Microsoft.Azure.ActiveDirectory.GraphClient.ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot,
async () => await GetAppTokenAsync());
// Look up a user in the directory by their UPN.
var upn = "srikanth#xxxxxx.onmicrosoft.com";
var userLookupTask = await adClient.Users.Where(
user => user.UserPrincipalName.Equals(
upn, StringComparison.CurrentCultureIgnoreCase)).ExecuteSingleAsync();
Thread.Sleep(30000);
User Userdata = (User)userLookupTask;
// Show John Doe's Name
Console.WriteLine(Userdata.DisplayName);
return userJohnDoe;
}
Any answer appreciated.
Thanks in Advance........!
I was trying to create a user in Azure AD without mail filed is user created successfully. I need to add the email id in Azure AD at the time of user created.
I added the mail property in json and it says
Property 'mail' is read-only and cannot be set.
My C# code is:
var url = string.Format("https://graph.windows.net/{0}/users?api-version=1.6",oauthsettings.TenantId);
var authDetails = _orchardServices.WorkContext.CurrentSite.As<AzureAuthenticationPart>();
var alogin = new AzureLogin();
var jwttoken = alogin.ServiceAuth(authDetails.ClientId, authDetails.ClientSecret);
var aadUser =new {
mail=email,
accountEnabled = true,
displayName = userName,
mailNickname = userName,
passwordProfile = new passwordProfile()
{
password = password,
forceChangePasswordNextLogin = authDetails.IsUpdatePwdNextLogin
},
userPrincipalName = userName + oauthsettings.DirectoryName,
};
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + jwttoken);
var modelval = Convert.ToString(JsonConvert.SerializeObject(aadUser));
var content = new StringContent(modelval, Encoding.UTF8, "application/json");
var result = client.PostAsync(url, content).Result;
Get Access Token from Azure AD After Login
JwtSecurityToken token = GetAccessToken(authDetails, code, returnUrl);
var claims = token.Claims;
return LogOn(claims, returnUrl);
Getting Email from JWT
public LogOnResponse LogOn(IEnumerable<System.Security.Claims.Claim> claims, string returnUrl)
{
var email = claims.FirstOrDefault(s => s.Type == "email").Value;
In this place I can't get the access token, because the user created time is not set the email in Graph API Request. I have another problem is this email id based only I was validate another site also, so I was required set the email in user created time.
I required email id for login in my application. i was integrate the Azure AD in existing application it's required for email.
Does anyone know how to set the email id in Azure AD for a user.
My Request in Postman. Response for Email Added in Request
Because the mail attribute is tied to Exchange Online, we don't permit you to write to that attribute unless you have an Exchange Online license. When you activate a license for the user, Exchange Online will update the field with the correct mailbox mail address during the creation of the user's mailbox. You can utilize "MailNickName" and " other emails" during the creation of a user. This field will also depend upon if it is a "local account (B2C)" or "work or school account".
I hope this answers your question concerning the "mail" attribute being "read-only"
There are two different fields for Email Addresses on an AAD User.
From the Graph API Reference:
mail
POST, GET ($filter)
The SMTP address for the user, for example, "jeff#contoso.onmicrosoft.com".
otherMails
POST, GET ($filter), PATCH
A list of additional email addresses for the user; for example: ["bob#contoso.com", "Robert#fabrikam.com"].
Note that you can only set the mail property when you initially create the user (POST), but you can update the otherMails property whenever you want (PATCH).
It seems like you should be using the otherMails property for your needs.
Im trying Azure AD B2C and I've added Google and Microsoft Identity Providers through Azure Portal.
When i try to login with Microsoft OR Google IP, i always receive following error message in the OnAuthenticationFailed-Handler:
AADB2C99002: User does not exist. Please sign up before you can sign in.
But when i'm using the "Local Account SignIn" Provided by Azure B2C everything is working fine. Do i missing something in my configuration ?
The following code snippet shows my OWIN Configuration.
private void ConfigureAuthentication(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
OpenIdConnectAuthenticationOptions options = new OpenIdConnectAuthenticationOptions
{
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
SecurityTokenValidated = context => {
return null;
}
},
Scope = "openid offline_access",
// The PolicyConfigurationManager takes care of getting the correct Azure AD authentication
// endpoints from the OpenID Connect metadata endpoint. It is included in the PolicyAuthHelpers folder.
ConfigurationManager = new PolicyConfigurationManager(
String.Format(CultureInfo.InvariantCulture, aadInstance, tenant, "/v2.0", OIDCMetadataSuffix),
new string[] { SignUpPolicyId, SignInPolicyId, ProfilePolicyId }),
// This piece is optional - it is used for displaying the user's name in the navigation bar.
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name",
},
};
app.UseOpenIdConnectAuthentication(options);
}
// This notification can be used to manipulate the OIDC request before it is sent. Here we use it to send the correct policy.
private async Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
PolicyConfigurationManager mgr = notification.Options.ConfigurationManager as PolicyConfigurationManager;
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, notification.OwinContext.Authentication.AuthenticationResponseRevoke.Properties.Dictionary[AzureB2C.PolicyKey]);
notification.ProtocolMessage.IssuerAddress = config.EndSessionEndpoint;
}
else
{
OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, notification.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary[AzureB2C.PolicyKey]);
notification.ProtocolMessage.IssuerAddress = config.AuthorizationEndpoint;
}
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
// The user's objectId is extracted from the claims provided in the id_token, and used to cache tokens in ADAL
// The authority is constructed by appending your B2C directory's name to "https://login.microsoftonline.com/"
// The client credential is where you provide your application secret, and is used to authenticate the application to Azure AD
string userObjectID = notification.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant, string.Empty, string.Empty);
ClientCredential credential = new ClientCredential(clientId, clientSecret);
// We don't care which policy is used to access the TaskService, so let's use the most recent policy
string mostRecentPolicy = notification.AuthenticationTicket.Identity.FindFirst(AzureB2C.AcrClaimType).Value;
// The Authentication Context is ADAL's primary class, which represents your connection to your B2C directory
// ADAL uses an in-memory token cache by default. In this case, we've extended the default cache to use a simple per-user session cache
AuthenticationContext authContext = new AuthenticationContext(authority, new NaiveSessionCache(userObjectID));
// Here you ask for a token using the web app's clientId as the scope, since the web app and service share the same clientId.
// The token will be stored in the ADAL token cache, for use in our controllers
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(notification.Code, new Uri(redirectUri), credential, new string[] { clientId }, mostRecentPolicy);
}
// Used for avoiding yellow-screen-of-death
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
_log.Error("AuthenticationFailed!\r\nError={0}\r\nErrorDescription={1}\r\n{0}",
notification.ProtocolMessage.Error,
notification.ProtocolMessage.ErrorDescription,
notification.Exception.ToString());
notification.HandleResponse();
notification.Response.Redirect("/Home/OpenIdError?message=" + notification.ProtocolMessage.ErrorDescription);
return Task.FromResult(0);
}
}
External identities first need to 'sign up' as well before signing in. During sign up the external identity is linked to B2C.
In the sign up page you can ask additional attributes for your users, like a customer number. You need this for external identies and for the Local Account users in B2C, no difference between the two.
This is different behaviour compared to adding an identity provider without B2C, where every login just works.
Edit: Like Konstantin mentioned, the new combined sign-up or sign-in policy solves this problem:
https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-reference-policies/#create-a-sign-up-or-sign-in-policy
I was running into the same issue, but was able to circumvent the user "Sign-Up" after user insertion. The issue turned out to be, that to have proper federation occur, the proper values need to be in place.
"identities": [
{
"signInType": "federated",
"issuer": "https://login.microsoftonline.com/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/v2.0",
"issuerAssignedId": "YYYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"
What was happening was that I was using "issuer": "myDomain.com" which was not resolving correctly to do a login; to which the user then had to "SignUp" via the federated IP.
By changing that from DNS readable name, to the MS login with my AD directories ID (the number provided when switching domain in Azure, XXXX-XXX ...) and the proper issuerAssignedId, from the originating AD issuer, it worked and the user was added.