I have built an API that uses appid and apikey for authentication. It works fine when I'm calling the API from other applications using the appid and apikey, but I'm having problems using the API functions from the /swagger URL. I can type in an apikey at the top of the swagger UI, but I can't seem to enter the appid anywhere.
I've been looking at other similar questions and tried out what was suggested there.
I'm running Swashbuckle v5.6.0 on a .Net Framework 4.7.2 web API application.
I have added
c.ApiKey("apiKey")
.Description("API Key Authentication") // first key
.Name("Authorization")
.In("header");
c.ApiKey("appId")
.Description("API Key Authentication") // second key
.Name("Authorization")
.In("header");
to my GlobalConfiguration.Configuration.EnableSwagger(c =>
and
c.EnableApiKeySupport("apiKey", "header");
c.EnableApiKeySupport("appId", "header");
to my .EnableSwaggerUi(c =>
(both in SwaggerConfig.cs)
When I'm calling the API from another application I'm using a DelegatingHandler that adds the apiKey and appId to the authorization part of the header, so what's what I want to do when calling the functions from the swagger UI (eg http://my.api.com/myapi/swagger). I have seen that there is a way to get an Authorization button added to the swagger UI and when you click that button you can add stuff like appId, but how do I get that button in my swagger UI? Or is there another way of doing this?
Update 1
I figured out that I need to implement IOperationFilter, mostly because it says so right there in a comment in SwaggerConfig (d'oh!):
// you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties
// according to your specific authorization implementation
public class AddRequiredAuthorizationHeaderParameter : 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 = "appId",
#in = "header",
type = "string",
required = true,
schema = new Schema { #ref = "amx"},
description = "appID",
});
}
}
With this I am able to add the appId key in the swagger UI when calling the function, and there's an apiKey field in the top so I figure that's all I need.
However, I can't seem to set the correct schema on the Authorization header (which you can see above I've tried to set to "amx" using schema = new Schema { #ref = "amx"}. When I do my request and then check my HttpAuthenticationContext.Request.Headers.Authorization.Scheme is not "amx" which I want, but the appID I type in when I do the call, so my authentication-process doesn't approve of it.
So now I'm just trying to figure out how to set the HttpAuthenticationContext.Request.Headers.Authorization.Scheme to "amx".
I have now solved this. It's not pretty because I made a special check for when the call is from swagger.
The "appId" and "apiKey" values that I added (see question above), are retrievable from the HttpAuthentication object. HttpAuthentication .Request.Properties["MS_HttpContext"] contains a HttpContextWrapper whose .Items["MS_HttpRequestMessage"] contains a HttpRequestMessage and in that hashtable I can get my "appId" and "apiKey" with the .GetValues() function.
Related
I have a Asp.Net Web API 2 using Token based authentication (OAuth2).
I have implemented Web API versioning using aspnet-api-versioning.
So now I have three different versions of my API. It's really great, I can now change V3 without affecting the current API.
But the /token endpoint is not versioned because it is not in my controller. It's in the Providers.
I searched but couldn't find anything helpful.
We can register more than one token endpoint in the Startup.Auth.cs
So here's what I did:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
AllowInsecureHttp = true, //Allow HTTP to send username password.
};
app.UseOAuthBearerTokens(OAuthOptions);
OAuthOptionsV3 = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/V3/Accounts/Token"),
Provider = new ApplicationOAuthProvider2(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
AllowInsecureHttp = true, //Allow HTTP to send username password.
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptionsV3);
So now I have different token endpoint for each version.
I'm not familiar with this particular setup, but this looks like middleware. There isn't quite enough information here to provide you with a specific answer, but your goals should be achievable in one of a few ways:
Option 1 - Use the Conventions API
If you authorization endpoint is actually a controller (though I think it is not), you can use the Conventions API like so:
services.AddApiVersioning(options =>
{
options.Conventions.Controller<OAuthController>().IsApiVersionNeutral();
}
Conventions was specifically meant to deal with a scenario where a controller might be externally defined and you don't have any control over the source code.
Option 2 - Use a Custom Convention
Middleware could create actions dynamically. As long as actions are actually produced, then you can use a custom IControllerConvention. You would be passed the ControllerModel which contains the actions you need to version. Assuming this is the correct behavior, you'd be looking for matching actions in the source model and then you can apply it to the controller conventions with something like:
public class MyConventions : IControllerConvention
{
public bool Apply(IControllerConventionBuilder controller, ControllerModel controllerModel)
{
var method = // TODO: resolve the target method from controllerModel
if (method == null)
{
return false;
}
controller.Action(method).IsApiVersionNeutral();
return false;
}
}
Option 3 - In Middleware
If this is pure middleware, API versioning isn't directly supported there. You can, however, support versioning on your own if the pipeline is composed properly. Specifically, API Versioning must come before other parts of middleware that need it. This usually happens automatically, but if you need to control registration, you need to change your setup to handle it manually like this:
services.AddApiVersioning(options => options.RegisterMiddleware = false);
// ... inside application setup
services.UseApiVersioning();
The API Versioning middleware doesn't really do much of anything special. It merely adds a pipeline feature. As long as that's before your other middleware, it will be available downstream like this:
var feature = context.Features.Get<IApiVersioningFeature>();
// the raw, unparsed API version, if any
var rawApiVersion = feature.RawApiVersion;
// the parse API version; will be null if no version is specified
// or the value cannot be parsed
var apiVersion = feature.ApiVersion;
// TODO: enforce versioning policies within the middleware
Option 4 - Use the API Explorer
If none of the previous approaches will work for you, you can leverage the API Explorer extensions for API Versioning to build your configuration (as above) from discovered APIs. This would have the advantage of not being hardcoded or require changes every time you release a new version.
Your application startup configuration would change to something like this:
public void Configure(IApplicationBuilder app, IApiVersionDescriptionProvider provider)
{
foreach (var description in provider.ApiVersionDescriptions)
{
var options = new OAuthAuthorizationServerOptions()
{
TokenEndpointPath = new PathString($"/api/{description.GroupName}/Accounts/Token"),
Provider = new ApplicationOAuthProvider2(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
AllowInsecureHttp = true,
};
app.UseOAuthBearerTokens(options);
}
}
In AWS API Gateway I am developing lambda function for custom authorizer using .NET Core. The API will receive api-key in query string and my custom authroizer will validate the key. Based on my understanding, after validation is done the lambda function needs to return IAM policy. The awslab blurprint does not have any example for .NET core. The only example i found so far is GrandmasRecipes that is using JWT Token.
I would like to know what IAM policy the lambda function needs to return and are there any corresponding .NET Core classes for request and response?
Update 1
So below is my code for custom lambda authorizer. However i would like know:
1> What should be PrincipalID. Currently i am just setting it to User
2>CheckAuthorization method gets all the keys from aws and only check the existence by comparing the key from the request. It should also check the Usage Plans and make sure the key from the request is configured in Usage Plans
3>The role that this Authorizer is executing under is attached to AmazonAPIGatewayAdministrator policy so that it can get API Keys, whats the minimum policy do i need for this role to validate api-key?
4>Is there any in-built method in AWSSDK to do validate api-key correctly?
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
namespace ApikeyAuthorizer
{
public class Function
{
public async Task<APIGatewayCustomAuthorizerResponse> FunctionHandler(APIGatewayCustomAuthorizerRequest authEvent, ILambdaContext context)
{
var key = authEvent.QueryStringParameters["key"];
bool authorized = await CheckAuthorization(key);
var authPolicy = new APIGatewayCustomAuthorizerResponse();
authPolicy.PrincipalID = "user";
authPolicy.PolicyDocument = new APIGatewayCustomAuthorizerPolicy();
authPolicy.PolicyDocument.Version = "2012-10-17";
authPolicy.PolicyDocument.Statement = new List<APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement>();
if (authorized)
{
var statement = new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement();
statement.Action = new HashSet<string>(new string[] { "execute-api:Invoke" });
statement.Effect = "Allow";
statement.Resource = new HashSet<string>(new string[] { "arn:aws:execute-api:us-east-1:xxxxx:*/*/GET/*" });
authPolicy.UsageIdentifierKey = key;
authPolicy.PolicyDocument.Statement.Add(statement);
}
else
{
var statement = new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement();
statement.Action = new HashSet<string>(new string[] { "execute-api:Invoke" });
statement.Effect = "Deny";
statement.Resource = new HashSet<string>(new string[] { "arn:aws:execute-api:us-east-1:xxxxx:*/*/GET/*" });
authPolicy.PolicyDocument.Statement.Add(statement);
}
return authPolicy;
}
public async Task<bool> CheckAuthorization(string key)
{
Amazon.APIGateway.AmazonAPIGatewayClient client = new Amazon.APIGateway.AmazonAPIGatewayClient();
var response = await client.GetApiKeysAsync(new Amazon.APIGateway.Model.GetApiKeysRequest()
{
IncludeValues = true
});
foreach (var apiKey in response.Items)
{
if (apiKey.Value == key)
{
return true;
}
}
return false;
}
}
}
You don't need to use a Lambda Authorizer to validate API Key neither it should be used for authorisation. You can do following to configure API key validation in API Gateway.
In you API Resources section, set API Key Required true for the methods where you want to enable it
Go to API Keys section, select Create API key from Actions dropdown and create a key
Go to Usage Plans section and create a new usage plan.
After you've created a usage plan click on it and then click API Keys tab. Here click Add API Key to Usage Plan and add the key you created in step # 2
Now click on Details tab then click Add API Stage. Select your API and stage that you want to restrict with API Key.
Your API methods are now required an x-api-key HTTP header where you've enabled it. When you request API endpoint make sure you add x-api-key header with same value you have created in step 2 above. If you don't add this header or put a wrong value you will get 403 Forbidden error.
curl -X PUT \
https://XXXX.XXXXX-api.ca-central-1.amazonaws.com/PROD/XXX-microservice \
-H 'Content-Type: application/json' \
-H 'x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' \
-d '{
"initData": "HI",
"name": "vaquar khan",
"likes": "Java"
}'
Security key validation taken care by API getaway so no lambda
authorizer required
Please dont create duplicate question just update old queston ,I have answred in your question here :
- How to validate API Key in AWS Lambda function
I'm trying to protect my webapi with IdentityServer and OpenID Connect using Autofac. I'm using OWIN. But for some reason I can't get claims of the user. It seems that AccessTokenValidation is not triggered at all. That makes me think there is something wrong in the order of my declarations at my startup. Here is my startup.
public class Startup {
public void Configuration(IAppBuilder appBuilder) {
// Add authentication
this.AddAuthentication(appBuilder);
HttpConfiguration config = new HttpConfiguration();
var container = CreateAutofacContainer();
var resolver = new AutofacWebApiDependencyResolver(container);
config.DependencyResolver = resolver;
WebApiConfig.Register(config);
config.EnsureInitialized();
// Register config - you can't add anything to pipeline after this
appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(config);
appBuilder.UseWebApi(config);
}
private static IContainer CreateAutofacContainer() {
var autofacBuilder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
// Register your Web API controllers.
autofacBuilder.RegisterApiControllers(assembly);
// For general logging implementation
autofacBuilder.RegisterType<ConsoleLogger>().As<ILogger>();
// Create empty usage context to be filled in OWIN pipeline
IUsageContext usageContext = new RuntimeUsageContext();
autofacBuilder.RegisterInstance(usageContext).As<IUsageContext>().SingleInstance();
// We need to get usage context builded
autofacBuilder.RegisterType<OIDCUsageContextProvider>().InstancePerRequest();
var container = autofacBuilder.Build();
return container;
}
private void AddAuthentication(IAppBuilder app) {
var options = new IdentityServerBearerTokenAuthenticationOptions();
options.Authority = "MYAUTHORITY";
options.RequiredScopes = new[] { "openid", "profile", "email", "api" };
options.ValidationMode = ValidationMode.ValidationEndpoint;
app.UseIdentityServerBearerTokenAuthentication(options);
// Add local claims if needed
app.UseClaimsTransformation(incoming => {
// either add claims to incoming, or create new principal
var appPrincipal = new ClaimsPrincipal(incoming);
// incoming.Identities.First().AddClaim(new Claim("appSpecific", "some_value"));
return Task.FromResult(appPrincipal);
});
}
I'm using hybrid flow and api is called from SPA-application. I've verified (by calling my identity server's endpoint directly) that access token is valid and there are claims available. I also downloaded IdentityServer.AccessTokenValidation project and attached it as a reference. When I set some breakpoints to methods in that project, they never get called. That is why I think there is something wrong with my startup and OWIN pipeline.
I've declared UsageContext in my startup. It is a class I'm using to collect claims and some configuration settings - to be injected to actual controllers. I think it would be nice way to handle this, so in controllers there is always valid UsageContext available.
I've read a lot of samples and examples but still haven't found exactly same situation. I'll appreciate any attempts to point me into right direction.
Regards,
Borre
Could it be your registration of UsageContext as a Singleton? You mention this class contains claims, so this object should be resolved once pr http request - shouldn't it?
It turned out that there was some mysterious line in AccessTokenValidation - library that didn't work. I use that library to get claims. After changing the line everything seemed to work.
So basically my question is closed now and stuff works. But I'm still not totally convinced this is the right way to do this.
Thanks John for your comments!
Instagram API;
https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=code
My code o
public void Login()
{
auth = new OAuth2Authenticator (
clientId: "3b5a1...be2f5",
scope: "basic+public_content+follower_list+comments+relationships+likes",
authorizeUrl: new Uri ("https://api.instagram.com/oauth/authorize/"),
redirectUrl: new Uri ("https://Instagramcallback.com/callback")
);
auth.Completed+= (sender, e) => {
//DismissViewController(true,null);
if(e.IsAuthenticated)
{
OAuth2Request request= new OAuth2Request("POST", new Uri("https://api.instagram.com/oauth/access_token"),null);
Account loggedInAccount=e.Account;
//save the account data for a later session
AccountStore.Create(this).Save(loggedInAccount,"Instagram");
// Take access_token out
}
else
{
// errors be showing at here
}
};
StartActivity (auth.GetUI (this));
-e.IsAuthenticated it will be false.->Invalid scope field
OAuth 2.0 authentication, so we create an OAuth2Authenticator. Authenticators are responsible for managing the user interface and communicating with authentication services.
Authenticators take a variety of parameters; in this case, the application's ID, its authorization scope, and Facebook's various service locations are required.
From instagram doc:
To request multiple scopes at once, simply separate the scopes by a
space. In the url, this equates to an escaped space (“+”).
So you should use SPACES not "+" to separate scope authorizations, as xamarin auth does already encode your scope.
...
scope: "basic public_content follower_list comments relationships likes",
...
+1 if it helped ty!
I'm developing a Web API with Asp 5 and reading some documents about Web API realize I need Bearer authorization.
After searching I can't find any document or sample that use authorization without Aspnet.Identity. I have my own membership and I don't want to use Identity
Should I use Identity library? or is there a way to implement authorization in my membership.
One little side question:
if I'm forced to use Identity how can I change EntityFramework to something like dapper or ADO.NET for my DBContext?
There's already a JWT Bearer middleware, you just need to write something that issues bearer tokens. That's a little more complicated, depending on what you use as your identity store, and as you indicate it's something custom, it's hard to advise on any approach. Creating JWT tokens isn't that hard though;
var now = DateTime.UtcNow;
// Creates new keys automatically, you'd want to store these somewhere
var aes = new AesCryptoServiceProvider();
var signingTokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(
new[]
{
new Claim(JwtRegisteredClaimNames.Aud, "YOURWEBSITEURL") }),
TokenIssuerName = "YourWebSite",
Lifetime = new Lifetime(now, now.AddHours(1)),
SigningCredentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(aes.Key),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256")
};
var token = signingTokenHandler.CreateToken(tokenDescriptor);
var tokenAsString = signingTokenHandler.WriteToken(token);
None of the authorization pieces depend on membership at all, they'll work with any authentication middleware. None of the documentation for authorization even refers to Identity at all.
There's an authorization workshop available. You can see in the source for that that no-one does identity appear, it's creating user principals on the fly and then storing them in cookies.
To issue your own JWT tokens, you can use OpenIddict:
project.json
{
"dependencies": {
// ...
"AspNet.Security.OAuth.Validation": "1.0.0-*",
"OpenIddict": "1.0.0-*",
"OpenIddict.EntityFrameworkCore": "1.0.0-*",
"OpenIddict.Mvc": "1.0.0-*"
}
}
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<DbContext>(options =>
{
// Configure the context to use an in-memory store.
options.UseInMemoryDatabase();
// Register the entity sets needed by OpenIddict.
// Note: use the generic overload if you need
// to replace the default OpenIddict entities.
options.UseOpenIddict();
});
services.AddOpenIddict(options =>
{
// Register the Entity Framework stores.
options.AddEntityFrameworkCoreStores<DbContext>();
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.AddMvcBinders();
// Enable the token endpoint.
options.EnableTokenEndpoint("/connect/token");
// Enable the password flow.
options.AllowPasswordFlow();
// During development, you can disable the HTTPS requirement.
options.DisableHttpsRequirement();
});
}
public void Configure(IApplicationBuilder app)
{
// Register the validation middleware, that is used to decrypt
// the access tokens and populate the HttpContext.User property.
app.UseOAuthValidation();
// Register the OpenIddict middleware.
app.UseOpenIddict();
app.UseMvcWithDefaultRoute();
}
}
AuthorizationController.cs
public class AuthorizationController : Controller
{
[HttpPost("~/connect/token"), Produces("application/json")]
public IActionResult Exchange(OpenIdConnectRequest request)
{
if (request.IsPasswordGrantType())
{
// Validate the user credentials.
// Note: to mitigate brute force attacks, you SHOULD strongly consider
// applying a key derivation function like PBKDF2 to slow down
// the password validation process. You SHOULD also consider
// using a time-constant comparer to prevent timing attacks.
if (request.Username != "alice#wonderland.com" ||
request.Password != "P#ssw0rd")
{
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
}
// Create a new ClaimsIdentity holding the user identity.
var identity = new ClaimsIdentity(
OpenIdConnectServerDefaults.AuthenticationScheme,
OpenIdConnectConstants.Claims.Name,
OpenIdConnectConstants.Claims.Role);
// Add a "sub" claim containing the user identifier, and attach
// the "access_token" destination to allow OpenIddict to store it
// in the access token, so it can be retrieved from your controllers.
identity.AddClaim(OpenIdConnectConstants.Claims.Subject,
"71346D62-9BA5-4B6D-9ECA-755574D628D8",
OpenIdConnectConstants.Destinations.AccessToken);
identity.AddClaim(OpenIdConnectConstants.Claims.Name, "Alice",
OpenIdConnectConstants.Destinations.AccessToken);
// ... add other claims, if necessary.
var principal = new ClaimsPrincipal(identity);
// Ask OpenIddict to generate a new token and return an OAuth2 token response.
return SignIn(principal, OpenIdConnectServerDefaults.AuthenticationScheme);
}
throw new InvalidOperationException("The specified grant type is not supported.");
}
}
Request
POST /connect/token HTTP/1.1
Host: localhost:7096
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=alice%40wonderland.com&password=P%40ssw0rd
Response
{
"token_type": "Bearer",
"access_token": "CfDJ8Ec0ZpniaHhGg0e0UUvOH9BWZSGrPoEwGd0_Lq2cse-T29YOq985IBiT5fEe5tTSgY1vxq2Z2ZJ7Ikwlpmh0Lrc4x9pqhqHBziUzsP_rkGZkn47TkNkOkzKCwZJZK5x-irH3HROwClFFTq0rgWdb8rZ2xriffNzsby4VwhxhN5soFD435KzmVYkdv-VuaLYo3QiSuexbRi2USVO9LK30vomAG6h2SAxZ7R-jYsXgf0f5gAmdYxg7w3yicv9v8DpUSBiGGRRfymTOnvGEsFJjGuuP8OlY5qzMs6wGaRWkOvCyV2CK_RZF_3TMs7LYCdMQ-dqWY5A03-03OmP8blKzlrKJMDZfrPQHuysbS931xxy8b3kjicfjNLmMHqzQzbUO4fecm4kY8PFnKozojDtqajfTp2bYhxS65bmVYROrswYeUWEKYR6LSdS1K__IDaLoMlLa-Wf6x1wjM2CchzgqbHRF0KEtdL5Ks88dAS44mp9BM6iUOEWyL7VkbazsBdlNciM5ZZB1_6qunufDW_tcaR8",
"expires_in": 3600
}
For more information, you can read this blog post I wrote about OpenIddict: http://kevinchalet.com/2017/01/30/implementing-simple-token-authentication-in-aspnet-core-with-openiddict/