I have an Identity Server which is built by using IdentityServer4.
I have 2 applications (1 .NET, 1 PHP) accessing resource each other and using this Identity Server to validate access token in request header.
In Identity Server application I add a client config as below
clients.Add(
new Client
{
ClientId = "myClientId",
ClientName = "My Client Name",
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = new List<string>
{
"php.es.api"
}
});
From .NET application I can get access token easily by calling method RequestClientCredentialsAsync with scope "php.es.api". Then add this bearer token and send request to PHP API.
The problem is I don't know whether IdentityServer4 has API so that PHP application can call it to authenticate the access token. I google and don't find any document mentions about this API.
Do I have to write new API in Identity Server application for PHP or other applications not .NET to validate token?
.NET application access resource from PHP application as below.
There is a standard endpoint for this called the introspection endpoint and it is supported by IdentityServer4. Your best bet is to find an oauth client in PHP that does this. If you are using self-contained bearer tokens you can validate tokens without the need for back-channel communication because the bearer tokens are signed by your provider and your provider has its keys listed in the discovery document required to validate the tokens (/.well-known/openid-configuration). I am not too familiar with PHP to point you in the right direction as to what cool libraries there might be out there
Related
I am working on integrating a C#/.Net Framework desktop application to Google Sheets API. I have registered my application at developers.google.com where I chose type Desktop and I received a clientId and a clientSecret.
On the client side I want to use the OAuth2 Authorization Code flow to obtain an access token on behalf of the user. I am using Google.Apis.Sheets.v4 NuGet package which exposes some abstractions for obtaining the authorization code and access token(eg: GoogleWebAuthorizationBroker.AuthorizeAsync) and for using Sheets API(eg: SheetsService) . But it requires both the Client Id and Client Secret in order to obtain the token.
Since this is a Desktop application how safe is to expose the Client Secret on the client side where all the users who installed my app are able to decompile the app and get the credentials? Is there any PKCE approach like for Web SPA where you don't need a client secret to obtain a token?
This a working version of the flow:
private static async Task<UserCredential> GetCredential()
{
var scopes = new[] { "https://www.googleapis.com/auth/spreadsheets" };
var codeReceiver = new LocalServerCodeReceiver();
Console.WriteLine(codeReceiver.RedirectUri);
var authorizeAsync = await Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "some-client-id",
ClientSecret = "some-client-secret"
},
scopes,
"user",
CancellationToken.None,
new FileDataStore("Google.Apis.Auth.OAuth2.Responses.TokenResponse-user2"),
codeReceiver: codeReceiver
);
return authorizeAsync;
}
var sheetsService = new Google.Apis.Sheets.v4.SheetsService(new BaseClientService.Initializer
{
HttpClientInitializer = await GetCredential(),
ApplicationName = "my-app-name",
});
var request = sheetsService.Spreadsheets.Values.Get(spreadsheetId, range);
But as you can see it requires ClientSecret to be provided. Without it, I get an error from Google API Authorization endpoint saying the ClientSecret is missing.
My expectation is for ClientSecret not to be required to obtain an access token for Desktop/Installed apps.
I'm not exactly sure why you think users of your desktop application will see your client credentials.
When you compile your application ensure that you're client id and secret are complied into it. Don't have it in a clear text settings file.
While it is true that someone could decompile your application and get your client credentials this is a accepted risk.
The only other option would be to configure an endpoint on your own domain which all requests for your desktop applikation would need to go through on startup to get the credentials. While this would work it leads to other issues being that if your delivery service goes down your application would not work also if the user doesn't have internet it wouldn't work either.
There is really no perfect solution for desktop apps and Google apis unfortunately
To be clear you need a client id and client secret for authorization token flow with desktop applications, there is no authorization flow that didn't require a secret
I have a web API written in ASP.NET and frontend written in Angular. Authentication on the API side can be configured to use either Windows Authentication, ADFS Authentication or JWT Bearer. But for a particular deployment, only one type of authentication is supported.
For the ADFS authentication I am using angular-oauth2-oidc on the Angular side.
On the API side, this is how I am configuring the ADFS authentication in the startup.cs file.
public void ConfigureAuth(IAppBuilder app)
{
app.UseActiveDirectoryFederationServicesBearerAuthentication
(new ActiveDirectoryFederationServicesBearerAuthenticationOptions
{
BackchannelCertificateValidator = new CertificateValidator(),
MetadataEndpoint = System.Configuration.ConfigurationManager.AppSettings["ida:ADFSMetadata"],
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = System.Configuration.ConfigurationManager.AppSettings["ida:Audience"],
ValidIssuer = System.Configuration.ConfigurationManager.AppSettings["ida:ValidIssuer"]
},
Provider = new OAuthBearerAuthenticationProvider()
{
OnValidateIdentity = async context =>
{
var identity = context.Ticket.Identity;
var emailAddress = identity.FindFirst(ClaimTypes.Email).Value;
using (AuthRepository repo = new AuthRepository())
{
var userManager = repo.GetUserManager();
var aspNetUser = userManager.FindByEmailAsync(emailAddress).GetAwaiter().GetResult();
if (aspNetUser != null)
{
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, aspNetUser.Id));
await ProfileManager.AddClaimsToIdentity(identity, aspNetUser.SelectedProfile);
}
}
}
}
});
}
What I want is to combine ADFS authentication with JWT Bearer in such a way that if the entered email address is associated with my company domain like "email#mycompany.com", it authenticates against the ADFS and if it's any other email, it uses JWT Bearer authentication.
I know different type of authentications can be combined in ASP.NET Core (example here) but my API is not built using the Core.
I have tried to find the same feature for ASP.NET but I couldn't find one. I would like to know if this is possible to achieve and if yes, I will really appreciate if someone can give me a pointer.
On this question:
What I want is to combine ADFS authentication with JWT Bearer in such a way that if the entered email address is associated with my company domain like "email#mycompany.com", it authenticates against the ADFS and if it's any other email, it uses JWT Bearer authentication.
As far as I know, it is possible.
Have you tried Microsoft Identity Platform? This is a specific Azure SDK for managing identities, including support for ADFS 3.0 in Windows Server 2012 and later.
It is also easier to use, because this MIcrosoft Identity Platform is using OpenIDConnect with OAuth 2.0, therefore you can use ADFS that has JWT bearer.
This is the official docs page: https://learn.microsoft.com/en-us/azure/active-directory/develop/
The landing page has various samples for you to get started. Since you are using .NET Core, the Microsoft Identity Platform SDK has sample that you can use is the sample using .NET Core:
https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-webapp
This is a github repo that contains various sample beside that tutorial:
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2
UPDATE 1:
Current documentation of ADFS in Windows Server 2016 only shows support for OAuth2/OpenIDConnect, and it is using JWT Bearer:
https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/development/ad-fs-openid-connect-oauth-concepts
I have searched for specific "ADFS Bearer Token Authentication" in the MS official documentation, and it's not available at all. All of the current doc is mentioning about ADFS 3.0 and later (including ADFS in Windows Server 2016 and 2019) is using OAuth2/OpenIDConnect.
I have a SPA application that communicates with my backend Web API using AAD v2 authentication. Now I'm developing a console app to call Microsoft Graph on behalf of the user signed into the SPA app.
I have a valid access token of the user (used to call backend Web API). I want to use this access token to request a new token for accessing MS Graph.
Here is the code of the console app for requesting a new access token with MS Graph scopes using MSAL.NET:
string clientId = "<clientId>";
string clientSecret = "<clientSecret>";
string accessToken = "<validAccessTokenForWebApi>";
string assertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
string[] scopes = new string[] { "User.Read", "Mail.Send" };
string graphAccessToken = null;
try
{
var app = ConfidentialClientApplicationBuilder
.Create(clientId).WithClientSecret(clientSecret).Build();
var userAssertion = new UserAssertion(accessToken, assertionType);
var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion)
.ExecuteAsync().GetAwaiter().GetResult();
graphAccessToken = result.AccessToken;
}
catch (MsalServiceException ex)
{
throw;
}
But when I call app.AcquireTokenOnBehalfOf() I get an exception:
AADSTS50013: Assertion failed signature validation. [Reason - The provided signature value did not match the expected signature value., Thumbprint of key used by client: 'BB839F3453C7C04068B078EDADAB8E6D5F382E76', Found key 'Start=06/04/2019 00:00:00, End=06/04/2021 00:00:00']
What is the reason? What is the right way of getting access token on behalf of a user?
UPDATE - why do I need console app?
I could call Graph API directly from my backend API, but some actions may be delayed by the user (e.g. send mail using Graph API after 30 minutes). That is why I need to do this using the console app that runs on schedule.
If you want to use OAuth 2.0 On-Behalf-Of flow, I think you do not need to develop a console application to call graph api. You can directly use your backend Web API application to acquire access token then call Microsoft Graph. According to my understanding, you just do these steps
Sign-in the user in the client application
Acquire a token to the Web API (TodoListService) and call
it.
The Web API then calls another downstream Web API (The Microsoft
Graph).
For more details, please refer to the sample.
Regarding how to get access token with on behalf flow in the console application, The detailed steps are as below.
Register the web api app
Register APP
Create Client secrets
Configure permissions to access Graph API
Configure an application to expose web APIs(Add scope for the api)
Register the SAP app
Register APP
Create Client secrets
Configure permissions to access web API
Configure known client applications for web API application
In the Azure portal, navigate to your Web api
app registration and click on the Manifest section.
Find the property knownClientApplications and add the Client IDs of the SAP applications
Get access token to call web api
GET https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?scope=<you web api scope> openid
&redirect_uri=<your sap app redirect url>
&nonce=test123
&client_id=<you sap app client id>
&response_type=id_token token
get access token with on behalf flow
REST API
POST https://login.microsoftonline.com/common/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id=<you web api client id>
&assertion=<you acess token you get in above steps>
&client_secret=<you app secret>
&scope=https://graph.microsoft.com/user.read
&requested_token_use=on_behalf_of
MSAL.net Code
string[] scopes = { "user.read" };
string accesstoken = "";
string appKey = "yor web api client secret";
string clientId = "your web api application id";
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(appKey)
.Build();
UserAssertion userAssertion = new UserAssertion(accesstoken,
"urn:ietf:params:oauth:grant-type:jwt-bearer");
var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion).ExecuteAsync().Result;
Console.WriteLine(result.AccessToken);
I have deployed a .NET Core Web API and provided access through Azure API Management. I now wish to secure the back end using OAuth2 Client Credentials flow.
I have added Azure AD Authentication as follows:
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options =>
{
Configuration.Bind("AzureAd", options);
});
I have created an App Registration and Client Secret in Azure AD and confirmed it is working by creating a token using the code below and calling the API.
var clientCred = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenAsync(resource, clientCred);
return result.AccessToken;
Is it possible to use this flow in Azure API Management? I would like Azure API Management to handle acquiring the token and passing in the header.
The closest I have found is the following article but this seems to involve the consumer of the API passing headers which seems to defeat the point of the API Management subscription functionality
https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad
At the moment the only way to do that at APIM side is to use send-request policy to do OAuth flow. That will require you to give APIM client id and secret, but you could use named values to store those securely.
I have created a Web API in Azure.
This Web API makes some calls in SharePoint Online. Some of the api calls are on-behalf-of.
This Web API works fine until 01.05.2018 - and it works fine on old app services, which were created before 01.05.2018.
A microsoft staff member said:
As part of our security hardening efforts we do not allow id_token
redemption for any application created after 2018-05-01 00:00:00.
During the log in process of adal, I got the id_token. The id_token has got the same value as the access_token:
When I call the web api, I will send this token as bearer token.
The Web API takes this token (string accessToken) and starts the method 'AcquireTokenAsync':
var clientID = ConfigurationManager.AppSettings["ClientID"];
var clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
var tenant = ConfigurationManager.AppSettings["Tenant"];
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new AuthenticationContext(
"https://login.microsoftonline.com/" + tenant);
var resource = new Uri(sharePointUrl).GetLeftPart(UriPartial.Authority);
var authResult = await authContext.AcquireTokenAsync(resource, appCred,
new UserAssertion(accessToken));
return authResult.AccessToken;
But in the line which calls 'AcquireTokenAsync' I have got the error message:
AADSTS240002: Input id_token cannot be used as 'urn:ietf:params:oauth:grant-type:jwt-bearer' grant
But where is the problem?
The problem is that you use the same application identity in the front-end and back-end, and MS does not allow you to use the Id token (which you use as an access token here because of the former) to get another access token.
A possible solution:
Register another application (the front-end JS app should be a Native app)
It should acquire an access token for your back-end API using either the API's client id or app Id URI as the resource
Then the API can exchange the access token for another access token
If this is a multi-tenant app, the migration is probably not going to be easy.
If it's single-tenant, then all should be possible.
Your front-end app should of course require permission to call your back-end API in Azure AD, and that permission should be granted.
Another solution would be to acquire the other access token in the front-end using ADAL.JS instead of using on-behalf-of in the back-end and attaching that to all API requests in addition to the Id token.