I have a working version of a Client/Server authentication using ADAL. However, it appears that the B2C AAD doesn't work well with ADAL when you want to use Local Accounts (that is, just a username or just an email address with no backing authenticator other than AAD). It appears the API we should be using for Local Accounts is the alpha release of MSAL. So far, so good. I'm able to create a local user using the Graph API and using the following code, I appear to be authenticating the local user 'joeconsumer#mycompany.com':
this.pca = new PublicClientApplication("a4828eaa-42f6-418a-8062-f857130b69ce");
AuthenticationResult result = await this.pca.AcquireTokenAsync(
new string[] { "a4828eaa-42f6-418a-8062-f857130b69ce" },
string.Empty,
UiOptions.ForceLogin,
null,
null,
"https://login.microsoftonline.com/" + "darkbondpublic.onmicrosoft.com",
"B2C_1_sign-in");
The problem is that I pass the security token from 'result.Token' back to the server using a custom security token mechanism in WCF. The code on the server, which used to work with ADAL, no longer seems to accept the security token from the above call:
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
Microsoft.IdentityModel.Tokens.SecurityToken securityToken = null;
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(userName, this.GetTokenValidationParameters(MetadataAddress), out securityToken);
Thread.CurrentPrincipal = claimsPrincipal;
The error message is:
Can anyone tell me what is going on here? Do I need a different method of authenticating on the server?
The metadata endpoint you config for Azure AD B2C tenant is incorrect. Here is the correct one for your reference:
https://login.microsoftonline.com/{tenantId}/v2.0/.well-known/openid-configuration?p=B2C_1_Sign_In
We can find the metadata for the specific policy from the new Azure portal like figure below.
And in the metadata should able to see the keys endpoint like below:
https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys?p={policy}
We can find the key with kid gfIKIH-yZ3phRHRyjnsHIqZMaePLGAELzPat0CNY4sA like below figure:
I think the problem is: you are sending request to V1 endpoint but AAD B2C uses V2 endpoint with the authority: https://login.microsoftonline.com/tfp/{tenantId}/{policyName}/v2.0/
Metadata for v2 endpoint is available at https://login.microsoftonline.com/tfp/{tenantId}/{policyName}/.well-known/openid-configuration
Can you update your Urls and make one more attempt?
To see an authority in Azure Portal select your policy, then:
Locate your Policy
Click "Edit"
Click "Token, session & SSO config"
Expand "Issuer (iss) claim"
Azure (uses V1 endpoint) and Azure AD B2C (uses V2 endpoint) use different set of keys to sign tokens, therefore it is important to download public keys from the right location - originally you downloaded it from V1 but instead need to use V2.
For me this endpoint worked:
https://{Azure domain}/{Azure tenant}/v2.0/.well-known/openid-configuration?p={Azure policy}
Related
My Azure Data Factory has private endpoint connection to CosmosDB and authenticates using System Assigned Managed Identity. The goal is to delete document from CosmosDB using https://learn.microsoft.com/en-us/rest/api/cosmos-db/delete-a-document called from web activity.
I created web activity in Data Factory and put the required headers following those documents
https://learn.microsoft.com/en-us/rest/api/cosmos-db/common-cosmosdb-rest-request-headers
https://learn.microsoft.com/en-us/rest/api/cosmos-db/access-control-on-cosmosdb-resources?redirectedfrom=MSDN
DELETE web activity:
I am using Azure Cosmos DB RBAC so my authorization header looks like this:
type=aad&ver=1.0&sig=token-from-oauth
To get a token I was following this post
https://medium.com/analytics-vidhya/azure-data-factory-retrieve-token-from-azure-ad-using-oauth-2-0-9a3ed3f55013
but I don't know where can I get the client_secret. I found my ADF in AAD under enterprise application so I guess client_id is application Id but there is no client secret to be found there.
get token web activity:
First obvious question is where can I find this client_secret?
The second one is why is this token needed at all? Why can't it just use managed identity for authenticating the request?
Where can I find this client_secret?
Go to azure active directory -> Inside mange go to app registration(if you not created a app registration create it ) -> Go to registered app -> Certificate & Secretes.
Why is this token needed at all? why can't it just use managed identity for authenticating the request?
Managed identities are a way to simplify the authentication process for applications running in Azure, but they do not apply to all scenarios when calling external APIs that require authentication. In these cases, you need to obtain an access token from Azure AD using a client secret.
I reproduce same thing in my environment. Follow below approach.
URL:https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token
Scope : https://cosmos.azure.com/.default
Body: grant_type=client_credentials&client_id=<client_id>&client_secret=<client_secret>&scope=scope : https://cosmos.azure.com/.default
After execution of web1 activity you will get like this bearer token:
Inside Web2 activity provide valid URL as per below syntax:
https://{databaseaccount}.documents.azure.com/dbs/{db-id}/colls/{coll-id}/docs/{doc-id}
Add dynamic content at header part as shown in the image:
Authorization: Bearer #{activity('Web1').output.access_token}
I have registered a application using the App Registration (Preview) Blade and added the Azure Service Management API as API Permissions I downloaded the MSAL based Sample from
https://github.com/azure-samples/active-directory-dotnet-webapp-openidconnect-v2
Now in startup.auth.cs if i change the Scope i.e keep openid and add https://management.azure.com and then run and try and Login with a Microsoft Account i get the following error
This Doesn't Look like a Work or School Email you cant Sign-in here with Personal Account use your work or School Account Instead.
if i remove the Scope for https://managment.azure.com and just keep Openid profile offline_access i get the Consent Screen and Login
new OpenIdConnectAuthenticationOptions
{
// The Authority represents the v2.0 endpoint - https://login.microsoftonline.com/common/v2.0
// The Scope describes the initial permissions that your app will need. See https://azure.microsoft.com/documentation/articles/active-directory-v2-scopes/
ClientId = clientId,
Authority = String.Format(CultureInfo.InvariantCulture, aadInstance, "common", "/v2.0"),
RedirectUri = redirectUri,
Scope = "openid https://management.azure.com/.default",
PostLogoutRedirectUri = redirectUri,
I am Expecting to have the user Login and Obtain a Token for management API , i am Looking for Reasons for getting the above Error is this Expected ? The Account that i am using exists in my directory as a Member . this works if i use a Managed user(user#tenant.onmicrosoft.com) to Login
Since personal MS accounts cannot be used to manage Azure subscriptions unless they are added to an Azure AD, you should use the organizations endpoint instead of common.
In v1 "common" meant any AAD tenant.
In v2 "common" means any AAD tenant + any personal MS account.
If you wanted only personal accounts, you can use "consumers".
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc#fetch-the-openid-connect-metadata-document
You can use "organizations" to allow any AAD tenant but disallow MS accounts.
Of course a user can just edit the URL and login with a personal MS account anyway, so you'll want to check the tenant id of the logged in user.
The tenant id for MS accounts is always 9188040d-6c67-4c5b-b112-36a304b66dad, per the docs: https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens#payload-claims.
So check the idp claim.
I am accessing SharePoint online list data using the following code but i get the following error.
The remote server returned an error: (403) Forbidden.
The web forms application is running in Azure and uses Azure authentication. I would like to use the same credentials for authentication
string siteUrl = "[url]";
string aadAppId = "[appid]";
string clientSecret = "[redacted]";
OfficeDevPnP.Core.AuthenticationManager authManager = new OfficeDevPnP.Core.AuthenticationManager();
ClientContext context = authManager.GetAzureADNativeApplicationAuthenticatedContext(siteUrl, aadAppId, appurl, null, AzureEnvironment.Production);
if (context != null)
{
Web web = context.Web;
context.Load(web);
context.ExecuteQuery();
}
I do not want to access the SharePoint using the user credentials.
I have enabled AAD Azure authentication on the web app. The web app authenticates with Azure credentials.
On the CORS setting i have also set the Domain Url to "https://domainname.sharepoint.com";
In the manage permissions section of AAD app I have given the app Permission to read and write sharepoint list and web data.
You example is attempting to retrieve a token as a native client application, which is an app that cannot keep a secret and cannot authenticate by itself. (That's why you'll notice you never use clientSecret.)
If you want to use OfficePnP, you can try one of the AuthenticationManager.GetAzureADAppOnlyAuthenticatedContext methods to authenticate in the app-only context. Note that they all require a certificate for authentication, rather than a secret.
I'm having a little trouble following how API Access delegate permissions work with azure active directory. I feel like i'm probably misunderstanding a key aspect of how AAD works.
Here is my set up
I have a Web Application let’s call it WebApp. I have created
an AAD for the Web Application and registered with a AAD App ID. Let’s
call it App ID A
I have a Web Api let’s call it ApiService. I have also created an AAD for it and registered with a AAD App ID. Let’s all it App ID B.
In AAD App ID A, I have updated the clicked on the API Access ->
Required Permissions -> Add (App ID B ; Web API) permissions
I’ve updated the manaifest in the AAD App ID B, to give consent to
knownClientApplications to include the client ID of the Web App
I’ve also enable oauth2AllowImplicitFlow to be true for both App’s
manifest.
What I’m trying to do is, A user signs into the web application sign. When it signs in, the user is able to acquire a token for the specific Web App App ID A. The user should be able to use that token and have access the Api Service with App ID B. I thought by configuring the whole API Access -> Required Permissions within the Web Application it would give me delegate permission with the logged in user to communicate with the Api Service WebApi.
When I examine the JWT token, I notice that there is a claim for Microsoft Graph, but not for the ApiService. Shouldn’t I be seeing a claim?
When I try to use the token, it reacts with a 404 authentication error.
Any advice appreciated,
Thanks,
Derek
UPDATE
In response to #joonasw
I actually looked at the example you wrote when i started.
https://joonasw.net/view/aspnet-core-2-azure-ad-authentication
In the example, the web application is initialized with:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("OpenIdConnect").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = ctx =>
{
return Task.CompletedTask;
}
};
});
In the HomeController, there is code to retrieve the token for the graph api
private async Task<string> GetAccessTokenAsync()
{
string authority = _authOptions.Authority;
string userId = User.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");
var cache = new AdalDistributedTokenCache(_cache, _dataProtectionProvider, userId);
var authContext = new AuthenticationContext(authority, cache);
//App's credentials may be needed if access tokens need to be refreshed with a refresh token
string clientId = _authOptions.ClientId;
string clientSecret = _authOptions.ClientSecret;
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync(
"https://graph.microsoft.com",
credential,
new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
From my understanding, when the user initially login to the web application it will trigger the OnAuthorizationCodeReceived() method where it will be using the clientId/clientSecret/resource of the web applicaiton. The token is stored in the distributed token cache under the key resource/client id.
In the example, GetAccessTokenAsync() is used to grab the token to access the graph API.
In my case, I was hoping to update that method to retrieve the token for the WebApi which has a different clientId/clientSecret/resoruce. In my case, it will AcquireTokenSilentAsync will throw an AdalTokenAcquisitionExceptionFilter because the token needed is not stored in the cache and in the AdalTokenAcquisitionExceptionFilter it will call try to reauthenticate
context.Result = new ChallengeResult();
which will redirect to the authentication page and then hits the AddOpenIdConnect() method. However, the openIdConnect is configured with the web app clientID/ClientSecret/Resource and will not store the new token properly. It will try to call GetAccessTokenAsync() again and the whole process will go in an infinite loop.
In the example, if you were to comment out the "Anthentication:resource" in app.settings, you will experience the same issue with the infinite loop. What happens is that you initially authenticate correctly with no resource specified. Then when you click on you try to get the token for microsoft graph which is a new resource, it can't find it in the cache and then tries to reauthenticate over and over again.
I also notice that the acquireAsyncAuthentication only returns a AuthenticationResult with a bearer tokentype. How would you get the refresh token in this case?
Any advice?
Thanks,
Derek
UPDATE (Solution)
Thanks to #jaanus. All you have to do is update the resource to the clientid of the web api and pass that into AcquireTokenSilentAsync. The web api id uri that you can get from the azure portal did not work.
Okay, so it seems there are multiple questions here. I'll try to make some sense of this stuff to you.
Adding the "Web App"'s client id to the "ApiService" knownClientApplications is a good idea.
It allows for consent to be done for both apps at the same time. This really only matters for multi-tenant scenarios though.
Now, your Web App will be acquiring access tokens at some point.
When it does, it must specify a resource parameter.
This parameter says to AAD which API you wish to call.
In the case of the "ApiService", you should use either its client id or Application ID URI (this is more common).
Depending on the type of your Web App, the access token is acquired a bit differently.
For "traditional" back-end apps, the Authorization Code Grant flow is usually used.
In this flow your back-end gets an authorization code after the user logs in, and your Web App can then exchange that code for the access token.
In the case of a front-end JavaScript app, you would use the Implicit Grant flow, which you have allowed (no need to enable it in the API by the way).
This one allows you to get access tokens directly from the authorization endpoint (/oauth2/authorize) without talking to the token endpoint as you usually have to.
You can actually get the access token right away after login in the fragment of the URL if you wish.
ADAL.JS makes this quite a lot easier for you if you are going in this route.
The reason you get the authentication error is because the access token is probably meant for Microsoft Graph API. You need to request an access token for your API.
An access token is always only valid for one API.
I'm in the process of implementing AAD single sign on in our application and we will be using the adal.js/adal-angular.js library with our MEAN stack application. Part of initializing the library is to call init() and provide the tenant and clientId
adalProvider.init(
{
tenant: "mycompany.onmicrosoft.com",
clientId: "05bdd8d7-XXXX-XXXX-XXXX-5f2769f8b9b6"
},
$httpProvider
);
for example.
If someone views the source and takes the tenant and clientId can they use that somehow in their own application maliciously?
Does AzureAD check the URL the request came from and block it if it's not the configured login url?
Seems as though the clientId is more like a public key but if the only 2 things needed for an app to trigger authentication with AzureAD is the tenant and clientId and those are exposed client side in source code that someone could use them to create a phishing site X or to grab id_tokens if the request is redirected back to their site X rather than the original site
Does Azure rely on the configured settings in the application setup and protect against this?
I'm still getting a grasp on the OpenID Connect and OAUTH 2.0 finer points so forgive me if this question has an obvious answer.
Adal.js uses the Implicit flow (as per OpenID connect / oAuth 2 specifications) and exposing the ClientID (and tenant id of AAD) doesn't pose any security risk.
While registering the Clients in Azure AD administration panel, we specify a Redirect URI for the client. This is the Application URL for which users will get redirected after successful authentication.
So even if a malicious client tries to use your clientid and tenant id, the users will be redirected back to the registered URI(which is your app) after authentication and not to the malicious site
In implicit flow the application doesn't collect any username / password, IDP/OP (i.e AAD) manages this part - so user credentials won't be compromised as well
**For other flow types (Authorization code, Client credentials,etc) we have something called client-password along with ClientID. This shouldn't be exposed to public clients.