Swagger oauth access token and refresh token implementation for api endpoints - swagger

There are some api endpoints which need access token in authorization header. I am looking for a way where Some endpoints need access token in header. Such endpoints have a button for authroization and clicking on that button, it should hit oauth endpoint and generate access token and refresh token. That access token should automatically added in header for such request and proceed request. If access token is expired then it should generate access token by saved refresh token. I belive i have to use both security definitions
securityDefinitions:
sample_auth:
type: oauth2
authorizationUrl: http://<auhtorizeationUrl>
tokenUrl: http://<tokenUrl>
flow: accessCode
scopes:
write: "modify sample in your account"
sample_api_key:
type: apiKey
in: header
name: Authorization
My project is in node.js. As a first step, I am trying with hardcoding access token in header with sample_api_key security using in my endpoint. I following code in swagger-ui index.html
function addApiKeyAuthorization(){
console.log('key ');
var key = encodeURIComponent( $('.input_apiKey_entry')[0].value );
console.log('key '+key);
if(key && key.trim() != "") {
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization( "Authorization", "Bearer " + key, "header" );
window.swaggerUi.api.clientAuthorizations.add( "sample_api_key", apiKeyAuth );
log( "Set bearer token: " + key );
}
}
$('.input_apiKey_entry').change(addApiKeyAuthorization);
and added in my swagger.yaml in that specific endpoint
security:
- sample_api_key: []
In swagger ui, It provides me Authorize button clicking on it provide me option of adding Authroization value in text field. After authroizing, trigger request send error that
{
"message": "unknown security handler: sample_api_key",
"code": "server_error",
"statusCode": 403
}
node.js code shows error of
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11)
at applyHeaders (/Volumes/Data/samplegit/sample-api/node_modules/cors/lib/index.js:146:15)
at applyHeaders (/Volumes/Data/samplegit/sample-api/node_modules/node_modules/cors/lib/index.js:142:11)
at applyHeaders (/Volumes/Data/samplegit/sample-api/node_modules/node_modules/cors/lib/index.js:142:11)

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.

Reproducing an ADAL.JS-authenticated request in Postman

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:

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