Reproducing an ADAL.JS-authenticated request in Postman - oauth-2.0

I have a .NET Web API and a small vanilla-JS app using ADAL.js, and I've managed to make them talk nicely to each-other and authenticate correctly.
If I console.log the token returned from adalAuthContext.acquireToken() and manually enter it as Authorization: Bearer {{token}} in Postman, I can also get a valid, authenticated, response from my backend.
However, I can't figure out how to configure Postman's built-in OAuth2.0 authentication UI to get me tokens automatically. I have managed to get tokens in several ways, but none of them are accepted by the backend.
How do I configure Postman to get a token the same way the ADAL.js library does?
For completeness, here's some code:
Backend configuration:
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters { ValidAudience = "<app-id>" },
Tenant = "<tenant>",
AuthenticationType = "WebAPI"
});
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
}
ADAL.js configuration:
const backendUrl = 'http://localhost:55476';
const backendAppId = '<app-id>';
const authContext = new AuthenticationContext({
clientId: backendAppId,
tenant: '<tenant>',
endpoints: [{ [backendAppId]: backendAppId }],
cacheLocation: 'localStorage'
});
Actually making a request:
authContext.acquireToken(backendAppId, (error, token) => {
// error handling etc omitted
fetch(backendUrl, { headers: { Authorization: `Bearer ${token}` } })
.then(response => response.json())
.then(console.log)
})

So since the Azure AD v1 endpoint is not fully standards-compliant, we have to do things in a slightly weird way.
In Postman:
Select OAuth 2.0 under Authorization
Click Get new access token
Select Implicit for Grant Type
Enter your app's reply URL as the Callback URL
Enter an authorization URL similar to this: https://login.microsoftonline.com/yourtenant.onmicrosoft.com/oauth2/authorize?resource=https%3A%2F%2Fgraph.microsoft.com
Enter your app's application id/client id as the Client Id
Leave the Scope and State empty
Click Request token
If you configured it correctly, you'll get a token and Postman will configure the authorization header for you.
Now about that authorization URL.
Make sure you specify either your AAD tenant id or a verified domain name instead of yourtenant.onmicrosoft.com.
Or you can use common if your app is multi-tenant.
The resource is the most important parameter (and non-standards-compliant).
It tells AAD what API you want an access token for.
In this case I requested a token for MS Graph API, which has a resource URI of https://graph.microsoft.com.
For your own APIs, you can use either their client id or App ID URI.
Here is a screenshot of my settings:

Related

Swashbuckle Swagger UI not sending client_secret and client_id to OAuth endpoint when using authentication form

I have a .NET 5 API project documented with SwaggerGen for which I'm trying to use Swashbuckle as the documentation UI. My auth provider is Auth0, so I'm looking to have the docs generate a JWT bearer token by making a valid OAuth2 call to the Auth0 /oauth/token endpoint. The Authorize button is appearing on the generated page and produces a form that asks the user for the client_id and client_secret, but when I press the Authorize button it issues a POST request that is missing client_id and client_secret. Specifically, it goes to the correct endpoint (/oauth/token) but has no query string parameters and only grant_type: client_credentials in the POST body. I can see this in the Chrome developer tools. Somehow the UI is just completely disregarding the values I've typed into the client_id and client_secret form fields.
Is there a trick to making the auth request use the values from the form? Here is the relevant part of my SwaggerGen configuration:
options.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme {
Type = SecuritySchemeType.OAuth2,
Name = "Bearer",
Description = "Authorization using the OAuth2 access token authorization flow",
Scheme = "Bearer",
In = ParameterLocation.Header,
Flows = new OpenApiOAuthFlows {
ClientCredentials = new OpenApiOAuthFlow {
TokenUrl = new Uri($"https://{_configuration["Auth0:HostedDomain"]}/oauth/token"),
AuthorizationUrl = new Uri($"https://{_configuration["Auth0:HostedDomain"]}/authorize")
}
}
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "OAuth2"
}
},
new List<string>()
}
});
Are you sure the Swagger UI does not send them, i.e. in the authorization header?
I had a similar problem because our OpenID server recognizes only client credentials (client_id, client_secret) sent in the body form and we have to select the correct "Client credentials location" option in the authorization dialog (Request body):
Then the client_id is sent in the request body correctly:

How can I set the Bearer token after authentication in Swagger UI

I have a REST API service provider, written in PHP. I tested it successfully in Postman, and it works properly there.
Now I am going to prepare API documentation for it, and I am using Swagger UI 3. I set it up properly and I can process Authorization with the top Authorize button.
After a successful login, I expect the respective Bearer token being set and used by the endpoints. But this is not gonna happen, when I try any endpoint, the REST server complains about lack of Authorization Header. I tested the network traffic, and there is no token along with the HTTP request.
My question is, how can I send the Bearer token in the header in Swagger UI, after successfully login using the Authorize button on the top? Is there any steps/process I should take to accompany the endpoint request with the token?
I used it in a .NET core project, and in the Startup file I had to put the following code part:
services.AddSwaggerGen(options =>
{
//authentication
var security = new Dictionary<string, IEnumerable<string>>
{
{"Bearer", new string[] { }},
};
options.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
In = "Header",
Description = "Please insert JWT into field",
Name = "Authorization",
Type = "apiKey"
});
options.AddSecurityRequirement(security);
}

Either scp or roles claim need to be present in the token using when application permissions to read sharepoint sites

I created an app in Azure and set it up to use Access and ID tokens.
I want to connect to different tenants and read SharePoint sites. Here are the permissions I've requested and received Admin Consent for:
For now, I have set up an App Secret but I do plan to move to a certificate later.
I have this code to get the access token and I do get an access token back:
const params = new URLSearchParams();
params.append("grant_type", "client_credentials");
params.append("scope", "https://graph.microsoft.com/.default");
params.append("client_id", process.env.client_id);
params.append("client_secret", process.env.client_secret);
var url = `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`;
const response = await fetch(url,
{
method: 'POST',
body: params,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}
);
However when I try to read the root site below
var url = "https://graph.microsoft.com/v1.0/sites?search=*";
const response = await fetch(url,
{
method: 'GET',
headers: { 'Authorization': `Bearer ${access_token}` }
}
);
I get this error:
error: {
code: 'AccessDenied',
message: 'Either scp or roles claim need to be present in the token.',
innerError: {
'request-id': 'ec47913f-2624-4d1c-9b27-5baf05ccebfd',
date: '2019-08-16T14: 15: 37'
}
}
I checked the token at https://jwt.io/ and indeed I do not see any entry for roles or scp.
It looks like I missed a step but I cannot figure out which step.
I am getting the token like this:
https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token
What am I doing incorrectly?
The first thing to understand is that you cannot receive both Application and Delegated permissions in the same token, it is an either/or scenario. Which type you receive depends entirely on which OAuth Grant you used to request the token:
Authorization Code and Implicit return Delegated tokens with an scp property
Client Credentials return Application tokens with a roles property
The second thing is that you've requested scopes to two different APIs. Based on what you've selected, you won't have access to SharePoint through the Microsoft Graph because you've only requested access to the legacy SharePoint API. More importantly, you've only requested the Delegated User.Read scope for Graph so when you use Client Credentials to obtain the token, that token won't have any permissions.
In order to obtain an Application token for reading SharePoint sites, you'll need Sites.Read.All Microsoft Graph Application permission selected.

Using Bearer Authentication with Azure Active Directory Access Tokens

I am using the Passport AAD project with the bearer strategy to protect my endpoints. After I receive tokens with the OIDC strategy when logging in, I can't seem to get the bearer strategy to validate the signature of the access token. I get:
authentication failed due to: invalid signature
I have no problems validating the id_token, but I would prefer not to use this for our client app if the id_token can't be refreshed with AAD. Also, when using jwt.io to test the validation with the published public keys, I see the same issue (can validate the id_token, but not the access_token).
Am I missing a step when grabbing the access token, or is there a gap in my understanding of how access tokens are validated?
Update with more details
I am requesting an access token from my tenant:
identityMetadata: https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/.well-known/openid-configuration,
responseType: 'id_token code'
Using the OIDCStrategy in the AAD Passport project.
const callbackOIDC = (iss, sub, profile, accessToken, refreshToken, params, done) => {
return done(null,{
profile,
accessToken,
refreshToken
});
};
passport.use(new OIDCStrategy(config.creds, callbackOIDC));
Then I run authenticate, shown below:
auth.adCallback = function (req, res, next) {
passport.authenticate('azuread-openidconnect', {
response: res,
resourceURL: 'https://graph.microsoft.com',
session: false
}, function (err, user, info) {
console.log(user.access_token);
})(req, res, next);
};
I think I may have been asking for a graph access token above by specifying the resource URL. If I remove that resource URL, I still get an access token, but the bearer strategy throws an invalid token error (instead of an invalid signature error). Is there a different resource URL I should be setting to match with my tenant and get the access token I'm looking for?
What access tokens are you requesting? If the access token is meant to be used against the Microsoft Graph, for example, it is the Graph's task to validate them- not your app's.
Can you expand on the exact scenario you are trying to implement, and at what point you need to refresh id_tokens?

invalid_scope error in access token for client credential flow

I am getting invalid_scope error in access token request for client credential flow. The error log states that "cannot request OpenID scopes in client credentials flow". I haven't requested for the open id scope. I don't know from where it came from. I need to generate access token using client credential flow.
Issue / Steps to reproduce the problem
API Resource definition.
public IEnumerable GetApiResources()
{
return new List {
new ApiResource
{
Name = "WidgetApi",
DisplayName = "Widget Management API",
Description = "Widget Management API Resource Access",
ApiSecrets = new List { new Secret("scopeSecret".Sha256()) },
Scopes = new List {
new Scope("WidgetApi.Read"),
new Scope("WidgetApi.Write")
}
}
};
}
Client Definition;
return new List
{
new Client
{
ClientId = "WidgetApi Client Id",
ClientName = "WidgetApi Client credential",
RequireConsent = false,
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret( clientSecret.Sha256())
},
// scopes that client has access to
AllowedScopes = { "WidgetApi.Read", "WidgetApi.Write"},
AccessTokenLifetime = 3600
};
}
Access token request body (key - value) using postman
grant_type client_credentials
response_type id_token
scope WidgetApi.Read WidgetApi.Write
client_secret xxxxxxxxxxxxxxxxxxxxxx
client_id WidgetApiClientId
Relevant parts of the log file
dbug: Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerConnection[4]
Closing connection to database 'IdentityServer4Db' on server 'localhost\SQLEXPRESS'.
dbug: IdentityServer4.EntityFramework.Stores.ResourceStore[0]
Found PssUserMgtApi.Read, PssUserMgtApi.Write API scopes in database
fail: IdentityServer4.Validation.TokenRequestValidator[0]
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx cannot request OpenID scopes in client credentials flow
fail: IdentityServer4.Validation.TokenRequestValidator[0]
{
"ClientId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"ClientName": "xxxxxxxxxxxxxxxxxxxxxxxxx",
"GrantType": "client_credentials",
"Scopes": "xxxxxxxxxx.Read xxxxxxxxxxxxx.Write",
"Raw": {
"grant_type": "client_credentials",
"response_type": "id_token",
"scope": "xxxxxxxxxxxx.Read xxxxxxxxxxxxx.Write",
"client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 5292.2873ms 400 application/json
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
Connection id "0HL51IVGKG792" completed keep alive response.
Since there is no user tagged in a client credential flow, normally, client credential is not intended to have a scope tagged to it, and many frameworks doesnt support it.
https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/ says :
scope (optional) :
Your service can support different scopes for the client credentials grant. In practice, not many services actually support this.
Check whether your client credential details are correct or not. You can also find this simple step by step explanation to configure client credential flow using this link
If you have this problem, just remove the 'openid' scope for a given client in the database in ClientScopes.
Actually the question already contains the answer:
grant_type client_credentials
response_type id_token
scope WidgetApi.Read WidgetApi.Write
client_secret xxxxxxxxxxxxxxxxxxxxxx
client_id WidgetApiClientId
The request of client_credentials type should be processed at token endpoint and must not require id_token as the flow is non-interactive. The redundant parameter is breaking the flow.
I get this error with IdentityServer4 2.1.3, but not with IdentityServer4 2.3.2. It seems, from the GitHub issues for the project, that it was fixed in 2.3:
https://github.com/IdentityServer/IdentityServer4/issues/2295#issuecomment-405164127

Resources