Swagger (C#) not adding Authorize header - swagger

I'm trying to get Swagger working with a client credentials flow.
builder.Services.AddSwaggerGen(c =>
{
c.SchemaFilter<SchemaFilter>();
c.SwaggerDoc("v1", new OpenApiInfo { Title = "The API", Version = "v1" });
var basePath = System.AppContext.BaseDirectory;
string docFileName = Directory.GetFiles(basePath, "*.xml").First();
var docFile = Path.Combine(basePath, docFileName);
if (File.Exists(docFile))
{
c.IncludeXmlComments(docFile);
}
// Makes the UI appear and be able to get a token.
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme()
{
Description = "Bearer token",
In = ParameterLocation.Header,
Name = "Authorization",
Scheme = "Bearer",
BearerFormat = "JWT",
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{
ClientCredentials = new OpenApiOAuthFlow()
{
AuthorizationUrl = new Uri("http://www.localhost:5000/connect/authorize"),
TokenUrl = new Uri("http://www.localhost:5000/connect/token"),
Scopes = new Dictionary<string, string>() { { "thescope", "The scope" } }
}
}
});
// Docs claim this is necessary; doesn't say what it does in addition to the above.
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme
},
Scheme = "Bearer", // https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml
In = ParameterLocation.Header
},
new List<string>()
}
});
On the Swagger web page an Authorize button appears and I can use the client_id and client_secret to get a token.
However, Swagger does not use the token. No Authorize header is added to any requests. Why?

Related

How to make API request from Google authentication in ASP.NET?

I do not understand how I can get the authorization code/access token to make a request.
This is my .AddGoogle():
builder.Services.AddAuthentication(o =>
{
o.DefaultChallengeScheme = "Google";
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie().AddGoogle("Google",options =>
{
IConfigurationSection auth = builder.Configuration.GetSection("Authentication:Google");
options.ClientId = auth["ClientId"];
options.ClientSecret = auth["ClientSecret"];
options.CallbackPath = "/Home";
options.AuthorizationEndpoint += "?prompt=consent";
options.SaveTokens = true;
options.Scope.Add(FitnessService.Scope.FitnessActivityRead);
});
So I log in / register, and then what?
I've tried this method
UserCredential credential;
var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer()
{
ClientSecrets = new ClientSecrets()
{
ClientId = configuration.GetSection("Authentication:Google")["ClientId"],
ClientSecret = configuration.GetSection("Authentication:Google")["ClientSecret"]
},
Scopes = new[] {
FitnessService.Scope.FitnessActivityRead,
FitnessService.Scope.FitnessActivityWrite,
FitnessService.Scope.FitnessSleepRead,
FitnessService.Scope.FitnessSleepWrite
},
});
var token = new TokenResponse
{
AccessToken = "",
RefreshToken = ""
};
credential = new UserCredential(flow, "user", token);
FitnessService fitnessService = new FitnessService(new BaseClientService.Initializer()
{
ApplicationName = "Exrecise App",
HttpClientInitializer = credential
});
var resp = await fitnessService.Users.Sessions.List("me").ExecuteAsync();
But how do I get the tokens to begin with?
I even tried HttpClient (by pasting the token from OAuthplayground)
HttpClient http = new HttpClient();
var resp = await http.SendAsync(new HttpRequestMessage()
{
RequestUri = new UriBuilder("https://www.googleapis.com/fitness/v1/users/me/sessions").Uri,
Headers =
{
{ "Authorization","Bearer " }
},
Method = HttpMethod.Get
});
return Ok(await resp.Content.ReadAsStringAsync());
Same problem, how do I get the access token from the currently signed in user?
I looked everywhere, but every solution is either outdated or is unclear or just doesn't work for some reason.

.net core 3 AddSwaggerGen (SWAGGER UI) with security, Missing the string: "Bearer " in the authorization header

I have the following swagger config in .net core3
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "My API",
Version = "v1"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
})
});
The problem is that the Authorization header is sent, but without the word Bearer in the begining:
Actual:
Authorization: eyJhbGciOiJIUzI...
Expected:
Authorization: Bearer eyJhbGciOiJIUzI...

Implement token authorization using Swagger UI in asp.net core 2.0

I want to use JWT bearer token authorization using Swagger in my application.
when I use Postman tool the authorization works fine. But when I try authorize using Swagger the controller method always return unauthorized even after passing the token.
Am I missing some line of code for accepting token? Code implemented is as follows.
public void ConfigureServices(IServiceCollection services)
{
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "XYZ API", Version = "v1", Description = "This is a API for XYZ client applications.", });
c.IncludeXmlComments(GetXmlCommentsPath());
c.AddSecurityDefinition("Bearer", new ApiKeyScheme { In = "header", Description = "Please paste JWT Token with Bearer + White Space + Token into field", Name = "Authorization", Type = "apiKey" });
//c.AddSecurityDefinition("basic", new BasicAuthScheme { Type = "basic" });
});
services.AddCors(config =>
{
var policy = new CorsPolicy();
policy.Headers.Add("*");
policy.Methods.Add("*");
policy.Origins.Add("*");
policy.SupportsCredentials = true;
config.AddPolicy("policy", policy);
});
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​)
.RequireAuthenticatedUser().Build());
});
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
ValidAudience = "Audience",
ValidIssuer = "Issuer",
// ClockSkew = TimeSpan.FromMinutes(0)
ClockSkew = TimeSpan.Zero
};
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("policy");
app.UseAuthentication();
app.UseMvc();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "XYZ API V1");
});
}
Controller
[Authorize("Bearer")]
[Route("api/[controller]")]
public class ValuesController : Controller
{
}
You also need to add the AddSecurityRequirement in swagger schema definition to indicate that the scheme is applicable to all operations in your API.
like below:
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
});
So, the complete definition will be:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "XYZ API", Version = "v1", Description = "This is a API for XYZ client applications."});
c.IncludeXmlComments(GetXmlCommentsPath());
c.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
In = "header",
Description = "Please paste JWT Token with Bearer + White Space + Token into field",
Name = "Authorization",
Type = "apiKey"
});
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
});
});

IdentityServer4 Implicit GrantType throws Unauthorized_Client Error

When I run my project and try to login to get an access token, I get an Unauthorized_Client error on the browser or when testing with Postman. I'm fairly new to IdentityServer. This is my Configuration:
Config.cs
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
// custom identity resource with some consolidated claims
new IdentityResource("custom.profile", new[] { JwtClaimTypes.Name, JwtClaimTypes.Email, "location" })
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
//new ApiResource("NuB.hAPI", "Hospital API")
new ApiResource
{
Name = "NuB.HospitalSearch",
ApiSecrets = { new Secret("F621F470-9731-4A25-80EF-67A6F7C5F4B8".Sha256()) },
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Email,
JwtClaimTypes.PhoneNumber,
JwtClaimTypes.Gender,
"NuB.HospitalSearch"
},
Scopes =
{
new Scope
{
Name = "NuB.HospitalSearch",
DisplayName = "Full access to Hospital Search App"
},
new Scope
{
Name = "openid",
DisplayName = "Read only access to Hospital Search App"
}
}
}
};
}
// clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
new Client
{
ClientId = "mvcWeb",
ClientName = "MVC Web Client",
AllowedGrantTypes = GrantTypes.Implicit,
AccessTokenType = AccessTokenType.Jwt,
RequireConsent = true,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"NuB.HospitalSearch"
},
AllowOfflineAccess = true
}
};
}
I am using ASP.NET Identity with EntityFrameworkCore with IdentityServer4 to do this. It may by a simple problem but kindly point me to the right direction.
Unauthorized_Client means that your client is trying to authencate to the Ids with a client that does not exist. You apear to have created a client called mvcWeb
That means that your client will need to use a client id of mvcWeb your client code should probably look something like this. Note options.ClientId = "mvcWeb";
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = settingsSetup.Authority;
options.RequireHttpsMetadata = false;
options.ClientId = "mvcWeb";
options.ClientSecret = "secret";
options.ResponseType = oidcConstants.ResponseTypes.CodeIdTokenToken;
options.Scope.Add("openid");
options.Scope.Add("profile");
});

Connecting with Javascript to Web API protected by IdentityServer3

I have a Asp.NET MVC / WebAPI project with an embedded IdentityServer3.
I want both MVC and WebAPI to be protected by the IdentityServer. So I have used Authorize attribute on both MVC controllers and API controllers.
When surfing to my test page (which is protected) I get redirected to the IdentityServer login page. I enter my username and password and get authenticated and redirected back.
On the page I have a button that triggers a GET from javascript, with my access token in the authorization header, to my protected API. But here it fails with a 401 Unauthorized.
I get the access token to javascript by rendering it to the page with Razor.
I have 1 client in the IdentityServer set to hybrid flow. MVC uses cookies, while the API uses bearer tokens.
On my API HttpConfiguration I have set SuppressDefaultHostAuthentication. If I remove that line everything works, but then it uses cookies for the API which I don't want.
I use only HTTP and RequireSsl=false for now to avoid potential certificate problems.
I have tried for days to get this to work but I'm getting nowhere.
I don't even know how to debug this to get to cause of the 401.
By now I recognize just about every page that google suggests when I search for help.
Any ideas what it could be or how to debug this?
Here is my Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Trace()
.CreateLogger();
// MVC client
string authBaseAddress = "http://localhost:50319/identity";
string tokenEndpoint = authBaseAddress + "/connect/token";
string userInfoEndpoint = authBaseAddress + "/connect/userinfo";
string redirectUri = "http://localhost:50319/";
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "hybrid_clients",
Authority = authBaseAddress,
RedirectUri = redirectUri,
ResponseType = "code id_token token",
Scope = "openid profile roles sampleApi offline_access",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
},
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// use the code to get the access and refresh token
var tokenClient = new TokenClient(
tokenEndpoint,
"hybrid_clients",
"secret");
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
// use the access token to retrieve claims from userinfo
var userInfoClient = new UserInfoClient(
new Uri(userInfoEndpoint),
tokenResponse.AccessToken);
var userInfoResponse = await userInfoClient.GetAsync();
// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.GetClaimsIdentity().Claims);
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);
},
// Attach the id_token for the logout roundtrip to IdentityServer
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.FromResult(0);
}
}
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
// web api
app.Map("/api", a =>
{
var config = new HttpConfiguration();
a.UseCors(CorsOptions.AllowAll);
a.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
//AuthenticationMode = AuthenticationMode.Active,
Authority = authBaseAddress,
RequiredScopes = new[] { "sampleApi" },
DelayLoadMetadata = true
});
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.MapHttpAttributeRoutes();
a.UseWebApi(config);
});
// Identity server
app.Map("/identity", idsrvApp =>
{
idsrvApp.UseCors(CorsOptions.AllowAll);
idsrvApp.UseIdentityServer(new IdentityServerOptions
{
SiteName = "Embedded IdentityServer",
SigningCertificate = LoadCertificate(),
Factory = new IdentityServerServiceFactory()
.UseInMemoryUsers(Users.Get())
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get()),
AuthenticationOptions = new IdentityServer3.Core.Configuration.AuthenticationOptions()
{
EnablePostSignOutAutoRedirect = true // Automatically redirects back to the client on signout
},
RequireSsl = false,
});
});
}
X509Certificate2 LoadCertificate()
{
return new X509Certificate2(
string.Format(#"{0}\bin\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");
}
}
My client
new Client
{
Enabled = true,
ClientName = "Hybrid Clients",
ClientId = "hybrid_clients",
Flow = Flows.Hybrid,
//AllowAccessTokensViaBrowser = false,
RedirectUris = new List<string>
{
"http://localhost:50319/"
},
PostLogoutRedirectUris = new List<string>
{
"http://localhost:50319/"
},
AllowedScopes = new List<string>
{
"openid",
"profile",
"email",
"roles",
"address",
"all_claims",
"sampleApi",
"offline_access"
},
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
AccessTokenType = AccessTokenType.Reference,
LogoutSessionRequired = true
},
My scopes
public static class Scopes
{
public static IEnumerable<Scope> Get()
{
var scopes = new List<Scope>
{
StandardScopes.OpenId,
StandardScopes.Profile,
StandardScopes.Email,
StandardScopes.Address,
StandardScopes.OfflineAccess,
StandardScopes.RolesAlwaysInclude,
StandardScopes.AllClaims,
new Scope
{
Enabled = true,
Name = "roles",
Type = ScopeType.Identity,
Claims = new List<ScopeClaim>
{
new ScopeClaim("role")
}
},
new Scope
{
Enabled = true,
DisplayName = "Sample API",
Name = "sampleApi",
Description = "Access to a sample API",
Type = ScopeType.Resource
}
};
return scopes;
}
}
My API
[Authorize]
public class SecuredApiController : ApiController
{
public IHttpActionResult Get()
{
var user = User as ClaimsPrincipal;
var claims = from c in user.Claims
select new
{
type = c.Type,
value = c.Value
};
return Json(claims);
}
}
Part of my Razor view
<button data-bind="click:callApi">Call API</button>
<span data-bind="text:apiResult"></span>
<script>
$(function() {
ko.myViewModel = new ClientAppViewModel('#ViewData["access_token"]');
ko.applyBindings(ko.myViewModel);
});
</script>
My JavaScript (KnockoutJS) that calls SecuredApi
function ClientAppViewModel(accessToken) {
var self = this;
self.accessToken = accessToken;
self.apiResult = ko.observable('empty');
self.callApi = function () {
console.log('CallApi');
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:50319/api/SecuredApi");
xhr.onload = function () {
self.apiResult(JSON.stringify(JSON.parse(xhr.response), null, 2));
};
xhr.setRequestHeader("Authorization", "Bearer " + self.accessToken);
xhr.send();
}
}
The Katana logs for the API are what you want. With these you will see why the API is returning a 401.
You can access these using the Microsoft.Owin log source (see Katana Documentation)

Resources