I have some endpoints in the API - /user/login, /products.
In Swagger UI I post email and password to /user/login and as a response I receive a token string.
Then, I can copy the token from the response and want to use it as Authorization header value in requests to all urls if it's present, and to /products as an example.
Should I create a text input manually somewhere on the Swagger UI page, then put the token there and somehow inject in the requests or are there tools to manage it in a better way?
You can add a header parameter to your request, and Swagger-UI will show it as an editable text box:
swagger: "2.0"
info:
version: 1.0.0
title: TaxBlaster
host: taxblaster.com
basePath: /api
schemes:
- http
paths:
/taxFilings/{id}:
get:
parameters:
- name: id
in: path
description: ID of the requested TaxFiling
required: true
type: string
- name: auth
in: header
description: an authorization header
required: true
type: string
responses:
200:
description: Successful response, with a representation of the Tax Filing.
schema:
$ref: "#/definitions/TaxFilingObject"
404:
description: The requested tax filing was not found.
definitions:
TaxFilingObject:
type: object
description: An individual Tax Filing record.
properties:
filingID:
type: string
year:
type: string
period:
type: integer
currency:
type: string
taxpayer:
type: object
You can also add a security definition with type apiKey:
swagger: "2.0"
info:
version: 1.0.0
title: TaxBlaster
host: taxblaster.com
basePath: /api
schemes:
- http
securityDefinitions:
api_key:
type: apiKey
name: api_key
in: header
description: Requests should pass an api_key header.
security:
- api_key: []
paths:
/taxFilings/{id}:
get:
parameters:
- name: id
in: path
description: ID of the requested TaxFiling
required: true
type: string
responses:
200:
description: Successful response, with a representation of the Tax Filing.
schema:
$ref: "#/definitions/TaxFilingObject"
404:
description: The requested tax filing was not found.
definitions:
TaxFilingObject:
type: object
description: An individual Tax Filing record.
properties:
filingID:
type: string
year:
type: string
period:
type: integer
currency:
type: string
taxpayer:
type: object
The securityDefinitions object defines security schemes.
The security object (called "security requirements" in Swagger–OpenAPI), applies a security scheme to a given context. In our case, we're applying it to the entire API by declaring the security requirement a top level. We can optionally override it within individual path items and/or methods.
This would be the preferred way to specify your security scheme; and it replaces the header parameter from the first example. Unfortunately, Swagger-UI doesn't offer a text box to control this parameter, at least in my testing so far.
In ASP.NET Web API, the simplest way to pass-in a header on Swagger UI is to implement the Apply(...) method on the IOperationFilter interface.
Add this to your project:
public class AddRequiredHeaderParameter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
operation.parameters.Add(new Parameter
{
name = "MyHeaderField",
#in = "header",
type = "string",
description = "My header field",
required = true
});
}
}
In SwaggerConfig.cs, register the filter from above using c.OperationFilter<T>():
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "YourProjectName");
c.IgnoreObsoleteActions();
c.UseFullTypeNameInSchemaIds();
c.DescribeAllEnumsAsStrings();
c.IncludeXmlComments(GetXmlCommentsPath());
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
c.OperationFilter<AddRequiredHeaderParameter>(); // Add this here
})
.EnableSwaggerUi(c =>
{
c.DocExpansion(DocExpansion.List);
});
}
In ASP.NET Core 2 Web API, using Swashbuckle.AspNetCore package 2.1.0, implement a IDocumentFilter:
SwaggerSecurityRequirementsDocumentFilter.cs
using System.Collections.Generic;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace api.infrastructure.filters
{
public class SwaggerSecurityRequirementsDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument document, DocumentFilterContext context)
{
document.Security = new List<IDictionary<string, IEnumerable<string>>>()
{
new Dictionary<string, IEnumerable<string>>()
{
{ "Bearer", new string[]{ } },
{ "Basic", new string[]{ } },
}
};
}
}
}
In Startup.cs, configure a security definition and register the custom filter:
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
// c.SwaggerDoc(.....
c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
{
Description = "Authorization header using the Bearer scheme",
Name = "Authorization",
In = "header"
});
c.DocumentFilter<SwaggerSecurityRequirementsDocumentFilter>();
});
}
In Swagger UI, click on Authorize button and set value for token.
Result:
curl -X GET "http://localhost:5000/api/tenants" -H "accept: text/plain" -H "Authorization: Bearer ABCD123456"
Also it's possible to use attribute [FromHeader] for web methods parameters (or properties in a Model class) which should be sent in custom headers. Something like this:
[HttpGet]
public ActionResult Products([FromHeader(Name = "User-Identity")] string userIdentity)
At least it works fine for ASP.NET Core 2.1 and Swashbuckle.AspNetCore 2.5.0.
Here's a simpler answer for the ASP.NET Core Web Api/Swashbuckle combo, that doesn't require you to register any custom filters. Third time's a charm you know :).
Adding the code below to your Swagger config will cause the Authorize button to appear, allowing you to enter a bearer token to be sent for all requests. Don't forget to enter this token as Bearer <your token here> when asked.
Note that the code below will send the token for any and all requests and operations, which may or may not be what you want.
services.AddSwaggerGen(c =>
{
//...
c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = "header",
Type = "apiKey"
});
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
});
//...
}
Via this thread.
This is how I achieved it in .NET 6
public class AddCustomHeaderParameter
: IOperationFilter
{
public void Apply(
OpenApiOperation operation,
OperationFilterContext context)
{
if (operation.Parameters is null)
{
operation.Parameters = new List<OpenApiParameter>();
}
operation.Parameters.Add(new OpenApiParameter
{
Name = "Custom Header",
In = ParameterLocation.Header,
Description = "Custom Header description",
Required = true,
});
}
}
And finally
services.AddSwaggerGen(c =>
{
c.OperationFilter<AddCustomHeaderParameter>();
});
For those who use NSwag and need a custom header:
app.UseSwaggerUi3(typeof(Startup).GetTypeInfo().Assembly, settings =>
{
settings.GeneratorSettings.IsAspNetCore = true;
settings.GeneratorSettings.OperationProcessors.Add(new OperationSecurityScopeProcessor("custom-auth"));
settings.GeneratorSettings.DocumentProcessors.Add(
new SecurityDefinitionAppender("custom-auth", new SwaggerSecurityScheme
{
Type = SwaggerSecuritySchemeType.ApiKey,
Name = "header-name",
Description = "header description",
In = SwaggerSecurityApiKeyLocation.Header
}));
});
}
Swagger UI will then include an Authorize button.
I ended up here because I was trying to conditionally add header parameters in Swagger UI, based on my own [Authentication] attribute I added to my API method. Following the hint that #Corcus listed in a comment, I was able to derive my solution, and hopefully it will help others.
Using Reflection, it's checking if the method nested down in apiDescription has the desired attribute (MyApiKeyAuthenticationAttribute, in my case). If it does, I can append my desired header parameters.
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) {
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
var attributes = ((System.Web.Http.Controllers.ReflectedHttpActionDescriptor)
((apiDescription.ActionDescriptor).ActionBinding.ActionDescriptor)).MethodInfo
.GetCustomAttributes(false);
if(attributes != null && attributes.Any()) {
if(attributes.Where(x => x.GetType()
== typeof(MyApiKeyAuthenticationAttribute)).Any()) {
operation.parameters.Add(new Parameter {
name = "MyApiKey",
#in = "header",
type = "string",
description = "My API Key",
required = true
});
operation.parameters.Add(new Parameter {
name = "EID",
#in = "header",
type = "string",
description = "Employee ID",
required = true
});
}
}
}
Update for OpenAPI 3, library Swashbuckle.AspNetCore. Correct code sample is provifded by this source: https://codeburst.io/api-security-in-swagger-f2afff82fb8e
For use with JWT Bearer correct code is:
services.AddSwaggerGen(c =>
{
// configure SwaggerDoc and others
// add JWT Authentication
var securityScheme = new OpenApiSecurityScheme
{
Name = "JWT Authentication",
Description = "Enter JWT Bearer token **_only_**",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer", // must be lower case
BearerFormat = "JWT",
Reference = new OpenApiReference
{
Id = JwtBearerDefaults.AuthenticationScheme,
Type = ReferenceType.SecurityScheme
}
};
c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{securityScheme, new string[] { }}
});
}
I've seen one article with similar code for OpenAPI 2 and lost many hours because that example missed Reference definition. This resulted that Swashbuckle generated incorrect definitions and missed to include Authorizeation header. So check carefully the OpenAPI version you use.
Golang/go-swagger example: https://github.com/go-swagger/go-swagger/issues/1416
// swagger:parameters opid
type XRequestIdHeader struct {
// in: header
// required: true
XRequestId string `json:"X-Request-Id"`
}
...
// swagger:operation POST /endpoint/ opid
// Parameters:
// - $ref: #/parameters/XRequestIDHeader
If you are working with Nest.js, it can be achieved by adding addBearerAuth() while setting up the swagger(probably in the main.ts).
...........
const config = new DocumentBuilder()
.setTitle('Your title')
.setDescription('Your description')
.setVersion('1.0')
.addBearerAuth() // Add here
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
...........
With this added, we can pass in the Bearer token from the Swagger UI as:
PS: You have to use Authguard in respective controllers to protect your routes.
Just updating this for Asp.Net Core 6 web api and Swashbuckle.AspNetCore 6.4.0. It was surprisingly easy. In my case the header is required for all action methods so I have Required true.
One, create a Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter:
public class IMyCustomFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
operation.Parameters = new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter()
{
Name= "MyCustomHeader",
In=ParameterLocation.Header,
Required=true
});
}
}
Two, tell Swagger to use it when you are configuring services:
builder.Services.AddSwaggerGen(config =>
{
//this causes Swagger to add an input so you can to add the value to header when you are executing an api method.
config.OperationFilter<IMyCustomFilter>();
});
Three, invoke Swagger as usual:
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
DISCLAIMER: this solution is not using Header.
If someone is looking for a lazy-lazy manner (also in WebApi), I'd suggest:
public YourResult Authorize([FromBody]BasicAuthCredentials credentials)
You are not getting from header, but at least you have an easy alternative.
You can always check the object for null and fallback to header mechanism.
In Feathers Swagger, to show the Authorize button on the top right in this case for BearerAuth, your config declaration must be done as such:
{
...config, // other config parameters
specs: {
info: {
title: 'My API',
description: 'My API Documentation',
version: '2.0',
},
components: {
securityDefinitions: {
BasicAuth: {
type: 'basic'
},
BearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT"
}
},
},
security: {
BearerAuth: []
},
},
}
Result
Related
I have created an asp.net core project now, and I am making a swagger verification token, all of which are fine, but how to verify without adding the beginning of Bearer, what should I do?
enter image description here
If you want to omit the "Bearer" prefix tag, you can do this:
services.AddSwaggerGen(config =>
{
////Name the security scheme
config.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization",
Name = "Authorization",
In = ParameterLocation.Header,
// Type = SecuritySchemeType.ApiKey,
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat="JWT"
});
config.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
//The name of the previously defined security scheme.
Id = "Bearer"
}
},
new List<string>()
}
});
Note : If using SecuritySchemeType.ApiKey – token part should be
appended with ‘bearer’.
Note – If using SecuritySchemeType.Http – token need to be used
without “bearer”
Result:
I have an ASP.NET Core Web API with Swagger integrated using Swashbuckle. I have successfully integrated authorization on Swagger UI using an operation filter, because I do not want to show padlock for anonymous APIs.
.OperationFilter<AuthorizeFilter>()
Inside the filter, I have registered basic auth security requirement for Swagger UI.
My problem is, even though authentication is happening in APIs on Swagger UI, I no longer see that nice authentication popup which is giving when click on the padlock icon.
Could someone answer, why I am not seeing the auth popup now?
Assuming you have some endpoints that protected with [Authorize] attribute (can also be put on the controller).
[Route("")]
public class HelloController : ControllerBase
{
[Authorize]
[HttpGet("secure")]
public IActionResult GetSomethingPrivate()
{
return Ok("secret");
}
[HttpGet("public")]
public IActionResult GetSomethingPublic()
{
return Ok("hey");
}
}
You need to define a security scheme suitable for your needs. But do not require it globally, instead add it inside an operation filter. Here I've added a simple token auth:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiPlayground", Version = "v1" });
c.AddSecurityDefinition("token", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
Name = HeaderNames.Authorization,
Scheme = "Bearer"
});
// dont add global security requirement
// c.AddSecurityRequirement(/*...*/);
c.OperationFilter<SecureEndpointAuthRequirementFilter>();
});
}
And here's the operation filter which references the token auth scheme we've just created. It checks if the endpoint needs authentication, then adds the requirement.
internal class SecureEndpointAuthRequirementFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any())
{
return;
}
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "token" }
}] = new List<string>()
}
};
}
}
When you run the app, it works as you expect:
So does the auth popup:
Bonus: using basic auth
Define a new security scheme with following values:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSwaggerGen(c =>
{
// ...
// basic auth scheme (username + password)
c.AddSecurityDefinition("basic", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "basic"
});
// dont add global security requirement
// c.AddSecurityRequirement(/*...*/);
c.OperationFilter<SecureEndpointAuthRequirementFilter>();
});
}
Then update the operation filter to reference basic auth scheme:
internal class SecureEndpointAuthRequirementFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any())
{
return;
}
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "basic" // <-- changed "token" -> "basic"
}
}] = new List<string>()
}
};
}
}
here's how the auth popup looks:
After logging in, requests include the correct Authorization header.
In my case, I am using JWT Token Authentication with .NET Core API. I Configure the swagger with the authorization token using the below code. This code will add global security requirements.
In Startup Class ConfigureServices Method.
//Swagger Configuration
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "API",
Version = "v2",
Description = "Your Api Description"
});
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme (Example: 'Bearer 12345abcdef')",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
And In Configure Method
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
});
After running the API project Authorize button will appear on the right side. On Click the authorize button Authorization popup open and then pass the token in the text box with 'Bearer token'.
Authorization working fine for me.
the Abdusco's answer is true but modify the Apply method like this for AllowAnonymousAttribute Methods in Authorized Controllers
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any() || context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AllowAnonymousAttribute>()
.Any())
{
return;
}
This question already has answers here:
ASP Net Core 2.2 add locker icon only to methods that require authorization - Swagger UI
(3 answers)
Closed 2 years ago.
I have an ASP.Net Core Rest Web API documented with Swashbuckles's Swagger generation (.net v5 and Swashbuckle.AspNetCore v5.6.3). It generates Swagger documentation and UI with OAS3 support.
Also my API uses JWT bearer tokens. So, I added this code to the swagger configuration:
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Description = "Put `bearer` keyword in front of token"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme()
{
Reference = new OpenApiReference()
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
},
Array.Empty<string>()
}
});
And as expected, it added the authorization capability to the Swagger UI:
But I also noticed a few padlocks next to every HTTP request.
They are unlocked before authorization:
And after authorization they lock:
How could I get these padlocks to signal if authorization is required or not (I think I've seen the same padlocks somewhere doing this and it seems pretty natural to them to do this kind of thing as well)?
Already tried something like that, but it did not work (request headers no longer contained the jwt token):
options.OperationFilter<SecurityRequirementsOperationFilter>();
I figured out that the problem is that my Swagger is using OAS3 and SecurityRequirementsOperationFilter depending on OAS2. I've tried looking for alternatives, but it looks like there are no similar tools for OAS3.
What should I do?
Should I forget this feature? But that looks like the only purpose of these locks.
Are there any ways to have this feature and stay with OAS3 (also I not sure if I really need OAS3 support that much).
After some research, I found the answer here: https://stackoverflow.com/a/61365691/13851956.
So the code is:
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Description = "No need to put the `bearer` keyword in front of token",
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT"
});
options.OperationFilter<AuthorizationOperationFilter>();
public class AuthorizationOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var actionMetadata = context.ApiDescription.ActionDescriptor.EndpointMetadata;
var isAuthorized = actionMetadata.Any(metadataItem => metadataItem is AuthorizeAttribute);
var allowAnonymous = actionMetadata.Any(metadataItem => metadataItem is AllowAnonymousAttribute);
if (!isAuthorized || allowAnonymous)
{
return;
}
operation.Parameters ??= new List<OpenApiParameter>();
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
},
Array.Empty<string>()
}
}
};
}
}
The result is that padlocks appears only next to actions that a marked with [Authorize] and not marked with [AllowAnonymous] attributes:
I'm trying to implement swagger into my Asp.Net Web API, and i'm running into a problem.
I'm using the password resource owner flow, and i'm having to add a work around in order to do this, which is covered in the following stack overflow question :-
Swagger/Swashbuckle: OAuth2 with Resource Owner Password Credentials Grant
I've got everything working, the Bearer token is added via javascript to the request header in the current browser window, but the api calls to the controller methods requiring authorization are still return "401 - Authorization Failed".
Here is the JavaScript that gets the bearer token and adds the header :-
$('#input_apiKey').change(function () {
var key = $('#input_apiKey')[0].value;
var credentials = key.split(':'); //username:password expected
$.ajax({
url: "http://localhost:42291/token",
type: "post",
contenttype: 'x-www-form-urlencoded',
data: "grant_type=password&username=" + credentials[0] + "&password=" + credentials[1],
success: function (response) {
var bearerToken = 'Bearer ' + response.access_token;
window.swaggerUi.api.clientAuthorizations.add('Authorization', new window.SwaggerClient.ApiKeyAuthorization('Authorization', bearerToken, 'header'));
window.swaggerUi.api.clientAuthorizations.remove('api_key');
alert("Login Succesfull!");
},
error: function (xhr, ajaxoptions, thrownerror) {
alert("Login failed!");
}
});
});
The Curl in the response in Swagger is :-
curl -X GET --header "Accept: application/json" --header
"Authorization: Bearer
NqlSG-WyTx2zkYE8xFklGyZWlQDZdsCKZBHruEXvX47N7PAzw4-jZ4eH5D0yFzQTXj13RwKFFt1rUZt2fzWj1vR5UR87wdlKC3YvsTojYV4-3DsWwY7qYRfiKPuM0j09c3X5lnrtlBVJ1rBRUH0TLjfw_yGxgoLBwOJl9xyC1YWNoPOe2nzL4lMOHodAnMem0IBMJmUo3Rt575tnWAbBsQXWhlImDIxCZXvkZdJtlXfIfBSUdY9gfRWL0ZjKbf7m2-yLzH0gpMAMuKaADmJlIudJc0d4SP1Nn2Kh2HuVH8CX4QgZuu4egl9N6rY2smorP2vBSC4_dC4CpmYYzOTu2wUnUhHDY2Q6NWl377ijDKwZLcW9jtD-2tBiEGmFuRV0mVGnh0zc4w9Ao9jPCdtrbSyGitgloBW-UG2bfyao3eE"
"http://localhost:42291/api/v1/claims"
I cant see anything wrong with this at all.
I've then used Postman to call the exact same URL call, using the same access token that was generated in the javascript call...
Guess what... it works fine.
EDIT
I've tried removing the authorization attribute from the controller, so that i can check the request as it hits the controller method.
looking in the request headers, the Authorization property is null.
Not sure why this is. the CURL suggests its been placed into the request.
EDIT 2
Ive included my Security Definitions:-
"securityDefinitions": {
"oauth2": {
"type": "oauth2",
"description": "OAuth2 Password Grant",
"flow": "password",
"tokenUrl": "http://localhost:42291/token",
"scopes": {}
}
}
EDIT 3
The cURL displayed in the Swagger UI for this api call, when exposed through cURL directly at the command line works without issue.
Now I'm completely confused.
I've managed to correct the problem. It was a simple type mismatch that has caused me days of grief.
In the onComplete.JS, i needed to create a key that matches the key presented in the swagger specification.
If you examine my code snippets above you will see that i created a key and called it "Authorization". But that does not match the named security definition "oauth2".
The working code :-
$('#input_apiKey').change(function () {
var key = $('#input_apiKey')[0].value;
var credentials = key.split(':');
$.ajax({
url: "http://localhost:42291/token",
type: "post",
contenttype: 'x-www-form-urlencoded',
data: "grant_type=password&username=" + credentials[0] + "&password=" + credentials[1],
success: function (response) {
var bearerToken = "Bearer " + response.access_token;
window.swaggerUi.api.clientAuthorizations.remove('api_key');
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", bearerToken, "header");
window.swaggerUi.api.clientAuthorizations.add('oauth2', apiKeyAuth);
alert("Login Succesfull!");
},
error: function (xhr, ajaxoptions, thrownerror) {
alert("Login failed!");
}
});
});
Just to explain this a bit further, you need to create an implementation of IOperationFilter so that swagger can determine which methods of the api require Authorizaion. When you have configured this correctly, you should see a security definition against each api call in the swagger specification :-
My implementation of IOperationFilter :-
public class AssignOAuth2SecurityRequirements : IOperationFilter
{
/// <summary>
/// Apply Security Measures.
/// </summary>
/// <param name="operation"></param>
/// <param name="schemaRegistry"></param>
/// <param name="apiDescription"></param>
/// <exception cref="NotImplementedException"></exception>
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
// Determine if the operation has the Authorize attribute
var authorizeAttributes = apiDescription.ActionDescriptor.GetCustomAttributes<AuthorizeAttribute>();
if (!authorizeAttributes.Any())
return;
// Initialize the operation.security property
if (operation.security == null)
operation.security = new List<IDictionary<string, IEnumerable<string>>>();
// Add the appropriate security definition to the operation
var oAuthRequirements = new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", Enumerable.Empty<string>() }
};
operation.security.Add(oAuthRequirements);
}
}
The authorization mechanism expects that each operation has a security assigned to it. If not present, the header won't be sent. Please share your spec file if you think it's properly assigned
I'm using Microsoft Owin and ASP.NET WebApi for authentication and authorization process for my client application. Also the authentication server is secured by HTTPS. I've read a few articles about using Microsoft Owin, one of them which I've chosen to implement is:
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
There are some differences between my project and that implementation:
I need to identify my client in case the request is sent by my application on the mobile phone, not any other devices or tools like Fiddler. I think one the options could be sending an application id by each request from the mobile application. But I don't know how and where should I verify the requests in authentication server application. This is really important for registering users:
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
I don't want to let unreliable devices, i.e. the clients except the mobile application, to call this method.
I need to let anonymous users to buy some products from the website, but I don't know what is the best practice to issue token for anonymous users without doing authentication.
If you want to identify your client and authorize it you can override the method ValidateClientAuthentication.
In Taiseer's example you have linked you will find some code:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
and a note which says:
As you notice this class inherits from class
“OAuthAuthorizationServerProvider”, we’ve overridden two methods
“ValidateClientAuthentication” and “GrantResourceOwnerCredentials”.
The first method is responsible for validating the “Client”, in our
case we have only one client so we’ll always return that its validated
successfully.
If you want to validate the client you have to put some logic in there.
Normally you would pass a clientId and a clientSecret in the header of your http request, so that you can validate the client's request with some database parameters, for example.
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
context.Rejected();
return;
}
try
{
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
else
{
// Client could not be validated.
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
}
}
catch (Exception ex)
{
string errorMessage = ex.Message;
context.SetError("server_error");
context.Rejected();
}
return;
}
In the sample above you will try to extract the client credentials sent in the header of your request:
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
and validated them:
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
if the client is sending a wrong request header you need to reject the request:
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
The method ValidateClientAuthentication is processed before GrantResourceOwnerCredentials. This way you can extend it and pass GrantResourceOwnerCredentials some extra information you might need there.
In one of my applications I've created a class:
class ApplicationClient
{
public string Id { get; set; }
public string Name { get; set; }
public string ClientSecretHash { get; set; }
public OAuthGrant AllowedGrant { get; set; }
public DateTimeOffset CreatedOn { get; set; }
}
which I use in ValidateClientAuthentication right after I've checked the clientId and the secret are ok:
if (clientId == "MyApp" && clientSecret == "MySecret")
{
ApplicationClient client = new ApplicationClient();
client.Id = clientId;
client.AllowedGrant = OAuthGrant.ResourceOwner;
client.ClientSecretHash = new PasswordHasher().HashPassword("MySecret");
client.Name = "My App";
client.CreatedOn = DateTimeOffset.UtcNow;
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
context.Validated(clientId);
}
As you can see here
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
I am setting a Owin variable which I can read later on. In your GrantResourceOwnerCredentials now you can read that variable in case you need it:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client");
...
}
Now, if you want to fetch the bearer token - which you're going to use for all the secure API calls - you need to encode your clientId and clientSecret (base64) and pass it in the header of the request:
An ajax request with jquery would look something like this:
var clientId = "MyApp";
var clientSecret = "MySecret";
var authorizationBasic = $.base64.btoa(clientId + ':' + clientSecret);
$.ajax({
type: 'POST',
url: '<your API token validator>',
data: { username: 'John', password: 'Smith', grant_type: 'password' },
dataType: "json",
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
xhrFields: {
withCredentials: true
},
headers: {
'Authorization': 'Basic ' + authorizationBasic
},
beforeSend: function (xhr) {
},
success: function (result) {
var token = result.access_token;
},
error: function (req, status, error) {
alert(error);
}
});
As you can see I've also added the username and password - with the grant type - in the body of the request:
data: { username: 'John', password: 'Smith', grant_type: 'password' }
so that the server will be able to validate the client (clientId + clientSecret) and the user (username + password).
If the request is successful you should get back a valid token:
oAuth.Token = result.access_token;
which you can store somewhere for the following requests.
Now you can use this token for all the requests to the api:
$.ajax({
type: 'GET',
url: 'myapi/fetchCustomer/001',
data: { },
dataType: "json",
headers: {
'Authorization': 'Bearer ' + oAuth.Token
},
success: function (result) {
// your customer is in the result.
},
error: function (req, status, error) {
alert(error);
}
});
Another thing you might want to add to your API during the startup is SuppressDefaultHostAuthentication:
config.SuppressDefaultHostAuthentication();
this is an extension method of HttpConfiguration. Since you're using bearer tokens you want to suppress the standard cookie-based authentication mechanism.
Taiseer has written another series of articles which are worth reading where he explains all these things.
I have created a github repo where you can see how it works.
The Web API is self-hosted and there are two clients: jQuery and Console Application.