I am referring to Multitenant-saas-app sample. I am trying to get an access token to access Graph API and then get access token silently and access the graph api again.
Get Authorisation Code with /common endpoint for the multi-tenant-app,
private string resourceID = "https://graph.windows.net";
string authorizationRequest = String.Format(
"https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&client_id={0}&resource={1}&redirect_uri={2}&state={3}",
Uri.EscapeDataString(ConfigurationManager.AppSettings["ida:ClientID"]),
Uri.EscapeDataString("https://graph.windows.net"),
Uri.EscapeDataString(this.Request.Url.GetLeftPart(UriPartial.Authority).ToString() + "/Onboarding/ProcessCode"),
Uri.EscapeDataString(stateMarker)
);
return new RedirectResult(authorizationRequest);
Redirected with authorisation code, (/Onboarding/ProcessCode)
ClientCredential credential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientID"],
ConfigurationManager.AppSettings["ida:Password"]);
AuthenticationContext authContext = new AuthenticationContext("https://login.windows.net/common/");
//Get token to access grapgh API
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(Request.Url.GetLeftPart(UriPartial.Path)), credential, resourceID);
AuthenticationHelper.token = result.AccessToken;
This works fine and I get the access token where I can access the AzureAD resources for the tenant.
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
IPagedCollection<IUser> pagedCollection = await client.Users.ExecuteAsync();
Now I try to get get the token for offline access from the token cache. This time I create AuthenticationContext for the tenant. (I tried /common as well)
This gets me a new accesstoken silently.
string resourceID = "https://graph.windows.net";
//Test
ClientCredential credential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientID"],
ConfigurationManager.AppSettings["ida:Password"]);
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/mytenant.net");
var auth = await authContext.AcquireTokenAsync(resourceID, credential);
var newToken = auth.AccessToken;
//Set the token for this session
AuthenticationHelper.token = auth.AccessToken;
Then I try to access the API as previous,
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
IPagedCollection<IUser> pagedCollection = await client.Users.ExecuteAsync();
and I get the following exception,
Error = "Authorization_RequestDenied": "Insufficient privileges to
complete the operation."
Am I doing anything wrong here?
Here is my app permissions,
To list the users using the Azure AD graph REST, we require the Read all users' basic profile or Read all users' full profiles if you were not a global admin in the tenant.
And if you were the Global Admin in the tenant, the Access the directory as the sign-in user should also work to list users rest API.
And more detail about the scopes about Azure AD graph, you can refer here.
And for the cache issue, since you didn't provide custom cache, it would use the default cache based on the platform. For example, if you were developing an .Net application, the cache is using the memory to store the object. So it only works before you restart the application.
Related
I have a ASP.Net application that uses Azure AD Authentication, howver i want to connect to the azure database using the loggedin users identity. "Active Directory Integrated is not working for this scenario.
When a user logs into my application using microsoft login page, the same user should be able to login ( not the application identity) to the database and perform some execution.
You can refer the code below:
public async Task<string> GetTokenForApplicationAsync()
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential clientcred = new ClientCredential(Startup.ClientId, Startup.AppKey);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the token cache
AuthenticationContext authenticationContext = new AuthenticationContext(Startup.AadInstance + tenantID, new ADALTokenCache(signedInUserID));
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync("https://database.windows.net/", clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return authenticationResult.AccessToken;
}
Here is an webapp sample which integrate with Azure AD and get access token for calling Microsoft Graph API. Just replace the GraphResourceId with https://database.windows.net/.
I want to access one API by its Client Credential directly not via any web application
private async Task<string> GetAutheticationToken(string APITypeSelected, string APIKeySelected=null)
{
string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
string tenant = ConfigurationManager.AppSettings["ida:AADTenant"];
string appKey = ConfigurationManager.AppSettings[APIKeySelected];
string apiID = ConfigurationManager.AppSettings[APITypeSelected];
//appKey = HttpUtility.UrlEncode(appKey);
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
using (HttpClient client = new HttpClient())
{
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = null;
ClientCredential clientCredential = null;
authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(authority);
//encodeURIComponent(client_secret);
clientCredential = new ClientCredential(apiID, appKey);
AuthenticationResult authResult = null;
authResult = await authContext.AcquireTokenAsync(apiID, clientCredential);
return authResult.AccessToken;
}
}
while executing I am getting bellow error(AADSTS501051) in this line
authResult = await authContext.AcquireTokenAsync(apiID, clientCredential);
AADSTS501051: Application '{API GUID}'(DEV-API) is not assigned to a
role for the application '{API GUID}'(DEV-API).
Do I have to give API permission to itself.
What I need to do.
Thanks,
First you need to make a user role for application if app assignment is required. if not there is no problem. If app assignment is required, Go back to api permission and in my api give permission for the created role, see Microsoft documentation url
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-registration
Ahh so you want an access token to the API itself? Not sure if that's possible..
If this in another app, it should be registered as another app in Azure AD.
It can then require application permissions on the API and call it via client credentials.
You can see how to define permissions here: https://joonasw.net/view/defining-permissions-and-roles-in-aad
If this is within the same app, it sounds odd that it would acquire a token for itself.
This error message indicates that you need to add an "App role" to your app registration. You can do so by first adding a new App role on {API GUID}
and then assign the app {API GUID} this role (don't forget to give admin consent)
Essentially what is happening here is that your app registration {API GUID} got a role on {API GUID} to create access tokens for the audience {API GUID}, so: itself.
When you use "authContext.AcquireTokenAsync(apiID, clientCredential);" to get the access token, you need to use identifierUri of your ad application as resource.
For example:
string tenantId = "your tenant id or name, for example: hanxia.onmicrosoft.com";
string clientId = "your client id";
string resource = "the identifierUri of your ad application ";
string clientSecret = "";
ClientCredentia clientCredentia = new ClientCredentia(clientId,clientSecret);
var context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId);
AuthenticationResult result = context.AcquireTokenAsync(resource, clientCredentia);
For more details, please refer to the document.
I have an Azure Account, now I'm trying to get token in an console application to manage resources (i.e. create a resource group etc):
string userName = "xyz#gmail.com";
string password = "XXXXXXXXX";
string directoryName = "xyzgmail.onmicrosoft.com";
string clientId = "guid-of-registered-application-xxx";
var credentials = new UserPasswordCredential(userName, password);
var authenticationContext = new AuthenticationContext("https://login.windows.net/" + directoryName);
var result = await authenticationContext.AcquireTokenAsync("https://management.core.windows.net/", clientId, credentials);
On AcquireTokenAsync call I have
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException:
'accessing_ws_metadata_exchange_failed: Accessing WS metadata exchange
failed'
Can anybody help, please?
Update: how I tried to create a resource group under newly created user
var jwtToken = result.AccessToken;
string subscriptionId = "XX-XX-XX-YY-YY-YY";
var tokenCredentials = new TokenCredentials(jwtToken);
var client = new ResourceManagementClient(tokenCredentials);
client.SubscriptionId = subscriptionId;
var rgResponse = await client.ResourceGroups.CreateOrUpdateWithHttpMessagesAsync("myresgroup77777",
new ResourceGroup("East US"));
Here I got another exception
'The client 'newaduser#xyzgmail.onmicrosoft.com' with object id
'aaa-aaa-aaa-aaa' does not have authorization to perform action
'Microsoft.Resources/subscriptions/resourcegroups/write' over scope
'/subscriptions/XX-XX-XX-YY-YY-YY/resourcegroups/myresgroup77777'.'
Not sure why you're getting the first error, but the second error is because the signed in user does not have permission to perform the operation (as mentioned in the error message).
When you assign the permission to execute Windows Azure Service Management API, it is actually assigned to the application which assumes the identity of the signed in user.
In order to perform Create Resource Group operation in Azure Subscription, that user must be in a role that allows this operation to be performed. You can try by assigning built-in Contributor role at the Azure Subscription level to this user.
Also, regarding using login.windows.net v/s login.microsoftonline.com, it is recommended that you use latter. When you use login.windows.net, it gets automatically redirected to login.microsoftonline.com. Using login.microsoftonline.com will save you one redirection.
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.
I create a Native client application. I have got access token result from Azure AD by the article given below:
https://msdn.microsoft.com/en-us/library/dn531009.aspx
code give below:
Uri serviceUrl = new System.Uri(CrmServiceUrl + "/XRMServices/2011/Organization.svc/web");
string _oauthUrl = "https://login.windows.net/00000000-0000-0000-0000-000000000000/oauth2/authorize"; // DiscoveryAuthority
String resource = "https://****.crm.dynamics.com/";
authenticationContext = new AuthenticationContext(_oauthUrl, false);
//custom test code
AuthenticationResult result = authenticationContext.AcquireToken(resource, _clientID, serviceUrl, PromptBehavior.Auto);
return result.AccessToken;
now my question is this, How i can create service proxy from that "Access token" to make CRUD operations?
is it possible to create record in dynamic crm using access token or not?
your response should be appreciated.
thanks.