I'm using Owin with JWTBearerAuthentication to authorize users and validate their tokens. I'm doing it like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
ConfigureOAuth(app);
app.UseWebApi(config);
}
private void ConfigureOAuth(IAppBuilder app)
{
string issuer = ConfigurationManager.AppSettings.Get("auth_issuer");
string audience = ConfigurationManager.AppSettings.Get("auth_clientId");
byte[] secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings.Get("auth_secret"));
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new [] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
}
}
However, I have some custom claims in my token, and want to use their values in my ApiController, which looks like this:
[RoutePrefix("endpoint")]
public class MyApiController : ApiController
{
[Route("action")]
[Authorize]
public IHttpActionResult Post(string someValue)
{
bool res = DoSomeAction.withTheString(someValue);
if (res)
{
return Ok<string>(someValue);
}
return InternalServerError();
}
}
Is there anything like User.Claims["myCustomClaim"].Value, which provides the values of all claims?
Thank you,
Lukas
Something like this might help:
var identity = User.Identity as ClaimsIdentity;
return identity.Claims.Select(c => new
{
Type = c.Type,
Value = c.Value
});
Related
I've added UseJwtBearerAuthentication middleware to my application to Authenticate all incoming requests:
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
WebApiConfig.Register(config);
#region Autofac config
var container =AutofacWebapiConfig.Initialize(GlobalConfiguration.Configuration);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
#endregion
#region RoutConfig
RouteConfig.RegisterRoutes(RouteTable.Routes);
#endregion
//Register middlewares
app.UseJwtBearerAuthentication(new MyJwtAuthenticationOptions());
app.UseAutofacMiddleware(container);
app.Use<ReadBodyMiddleware>();
app.UseWebApi(config);
}
And this is my MyJwtAuthenticationOptions class:
public class MyJwtAuthenticationOptions: JwtBearerAuthenticationOptions
{
public MyJwtAuthenticationOptions()
{
var secretkey = ConfigurationManager.AppSettings["SecretKey"].ToString();
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active;
AuthenticationType = "Basic";
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretkey)),
ValidAlgorithms = new string[] { SecurityAlgorithms.HmacSha256Signature }
};
}
}
Now let's see how token is generated:
public static string GenerateToken(string userid)
{
var expireMin = ConfigurationManager.AppSettings["TokenExpirationMinutes"].ToString();
var secretKey = ConfigurationManager.AppSettings["SecretKey"].ToString();
byte[] key = Convert.FromBase64String(secretKey);
SymmetricSecurityKey securityKey = new SymmetricSecurityKey(key);
var descriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] {
new Claim("UserId", userid)}, "Basic"),
Expires = DateTime.UtcNow.AddMinutes(Convert.ToInt32(expireMin)),
SigningCredentials = new SigningCredentials(securityKey,
SecurityAlgorithms.HmacSha256Signature)
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
JwtSecurityToken token = handler.CreateJwtSecurityToken(descriptor);
return handler.WriteToken(token);
}
But when i put generated token inside Authorization header and send it to server via Postman
HttpContext.Current.User.Identity.IsAuthenticated is always false
Authorization header is correctly in Bearer format
My issue was originated from two points:
point1:
public static void Register(HttpConfiguration config)
{
// Owin auth
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
config.MapHttpAttributeRoutes();
config.Services.Insert(typeof(ModelBinderProvider), 0,
new SimpleModelBinderProvider(typeof(DocumentModel), new FromFormDataBinding()));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
this line was not same as other points of my api that i used Signature
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
Changed to this:
config.Filters.Add(new HostAuthenticationFilter("Signature"));
point2:
Inside GenerateTokebn class i was reading secretkey in this way:
byte[] key = Convert.FromBase64String(secretKey);
changed to this line to be same as MyJwtAuthenticationOptions class:
byte[] key = Encoding.UTF8.GetBytes(secretKey);
How to save user data in pure Web Api application, throughout the entire application life such as Session, So that on each request we can use the saved user data.
I saw that in WEB API each request is separate and has no connection to the previous request and therefore can not use Session.
Can anyone help me?
You need to install Microsoft.Owin from Nuget. Then Add this in your start up class.
public void ConfigureAuth(IAppBuilder app)
{
var OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
}
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
Then need to add a provider like
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated(); //
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (var db = new TESTEntities())
{
if (db != null)
{
var empl = db.Employees.ToList();
var user = db.Users.ToList();
if (user != null)
{
if (!string.IsNullOrEmpty(user.Where(u => u.UserName == context.UserName && u.Password == context.Password).FirstOrDefault().Name))
{
identity.AddClaim(new Claim("Age", "16"));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"userdisplayname", context.UserName
},
{
"role", "admin"
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
else
{
context.SetError("invalid_grant", "Provided username and password is incorrect");
context.Rejected();
}
}
}
else
{
context.SetError("invalid_grant", "Provided username and password is incorrect");
context.Rejected();
}
return;
}
}
}
You can add number of claim if you required. Then Modify your WebApiConfig
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
EnableCorsAttribute cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
Then test your token like
Then you pass your token by authorization header. Then get your claims.
Sample of api request
Sample code for get claims data
var principal = this.Request.GetRequestContext().Principal as ClaimsPrincipal;
var claims = principal.Claims.ToList();
var age = claims.FirstOrDefault(c => c.Type == "Age")?.Value;
You can use session variable such as:
Session["FirstName"] = FirstNameTextBox.Text;
Session["LastName"] = LastNameTextBox.Text;
To use session variables :
// When retrieving an object from session state, cast it to
// the appropriate type.
ArrayList stockPicks = (ArrayList)Session["StockPicks"];
// Write the modified stock picks list back to session state.
Session["StockPicks"] = stockPicks;
For mor informations go to : MSDN
I have gone through a lot of docs but it seems my problem is strange.
I have configured Oauth but I am not able to get the bearer token back. whenever I hit api to get the token, I get 200 but nothing back in response(I am expecting bearer token). Below is the config:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions oAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),
Provider = new ApplicationOAuthProvider()
};
app.UseOAuthAuthorizationServer(oAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new OAuthBearerAuthenticationProvider()
});
HttpConfiguration config = new HttpConfiguration();
//config.Filters.Add(new );
//config.MapHttpAttributeRoutes();
// There can be multiple exception loggers. (By default, no exception loggers are registered.)
//config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
WebApiConfig.Register(config);
//enable cors origin requests
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
}
public static class WebApiConfig
{
/// <summary>
///
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Filters.Add(new HostAuthenticationAttribute("bearer")); //added this
config.Filters.Add(new AuthorizeAttribute());
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var form = await context.Request.ReadFormAsync();
if (myvalidationexpression)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Role, "AuthorizedUser"));
context.Validated(identity);
}
else
{
context.SetError("invalid_grant", "Provided username and password is incorrect");
}
}
}
Now when I launch the APi and hit /token, I get this as below:
API Request
I think that code you have written in WebApiConfig.cs to suppress host authentication and some other code is creating the issue.
I have a working example for bearer token generation in web API, which is working properly and generating token.
WebApiConfig.cs file code:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Startup.cs Code:
[assembly: OwinStartup(typeof(WebAPI.Startup))]
namespace WebAPI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions
OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
Provider=new ApplicationOAuthProvider(),
//AuthenticationMode = AuthenticationMode.Active
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
Provider = new OAuthBearerAuthenticationProvider()
}
);
}
}
}
Controller to check authorization call after adding bearer token in the request.
public class TokenTestController : ApiController
{
[Authorize]
public IHttpActionResult Authorize()
{
return Ok("Authorized");
}
}
install the following package
Microsoft.Owin.Host.SystemWeb
I developed an api with OWIN authentication.
StartUp.cs
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(10),
Provider = new MyOAuthServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
In WebApi.Config
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
In MyOauthServerProvider
public class CRMnextOAuthServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
await Task.FromResult(context.Validated());
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (!IsAuthenticated(context.UserName, context.Password))
{
context.SetError(
"invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("user_name", context.UserName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties(new Dictionary<string, string> ());
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
}
I use above code for owin set up. this code is running fine on my local IIS, Testing port, and one public IP(locally).
Problem
when i deployed it on production server, i am able to access the token url, but not any controller url. it shows below error on server:
I have gone through loads of documentation on this, My google search shows that I've visited all the links on the first page
Problem
Token Generation works fine. I configured it with a custom provider as such:
public void ConfigureOAuth(IAppBuilder app)
{
var usermanager = NinjectContainer.Resolve<UserManager>();
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new AppOAuthProvider(usermanager)
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
However when I call a protected URL and pass the bearer token, I always get:
How do I diagnose or fix the problem. If possible, how can I do the token validation myself
UPDATE
Here is my AppOAuthProvider. Both Methods are called when I'm trying to mint a token but not when I'm trying to access a protected resource
public class AppOAuthProvider : OAuthAuthorizationServerProvider
{
private UserManager _user;
public AppOAuthProvider(UserManager user)
{
_user = user;
}
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
//Get User Information
var getUser = _user.FindUser(context.UserName);
if (getUser.Status == StatusCode.Failed)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return Task.FromResult<object>(null);
}
var user = getUser.Result;
//Get Roles for User
var getRoles = _user.GetRoles(user.UserID);
if (getRoles.Status == StatusCode.Failed)
{
context.SetError("invalid_grant", "Could not determine Roles for the Specified User");
}
var roles = getRoles.Result;
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("UserID", user.UserID.ToString()));
identity.AddClaim(new Claim("UserName", user.UserName));
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
context.Validated(identity);
return Task.FromResult<object>(null);
}
}
UPDATE 2:
Here is my Account Controller
[RoutePrefix("api/auth/account")]
public class AccountController : ApiController
{
private UserManager _user;
public AccountController(UserManager user)
{
_user = user;
}
[Authorize]
[HttpGet]
[Route("secret")]
public IHttpActionResult Secret()
{
return Ok("Yay! Achievement Unlocked");
}
}
UPDATE 3:
Here is my Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseNinjectMiddleware(NinjectContainer.CreateKernel);
app.UseNinjectWebApi(GlobalConfiguration.Configuration);
GlobalConfiguration.Configure(WebApiConfig.Register);
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(GlobalConfiguration.Configuration);
app.UseWelcomePage();
}
}
You must configure the OAuth authorization server and OAuth bearer authentication before call UseWebApi on IAppBuilder. The following is from my program.
public void Configuration(IAppBuilder app)
{
app.UseFileServer(new FileServerOptions()
{
RequestPath = PathString.Empty,
FileSystem = new PhysicalFileSystem(#".\files")
});
// set the default page
app.UseWelcomePage(#"/index.html");
ConfigureAuth(app);
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute
(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.JsonFormatter.SerializerSettings =
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new YourApplicationOAuthProvider()
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication
(
new OAuthBearerAuthenticationOptions
{
Provider = new OAuthBearerAuthenticationProvider()
}
);
}
HttpConfiguration config = new HttpConfiguration();
app.UseNinjectMiddleware(NinjectContainer.CreateKernel);
app.UseNinjectWebApi(GlobalConfiguration.Configuration);
ConfigureOAuth(app);
WebApiConfig.Register(config);
//GlobalConfiguration.Configure(WebApiConfig.Register);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// app.UseWebApi(GlobalConfiguration.Configuration);
app.UseWebApi(config);
app.UseWelcomePage();
I tried this with ur sample application on github and it worked
In your provider you have to:
public override ValidateClientAuthentication(OAuthClientAuthenticationContext context)
{
//test context.ClientId
//if you don't care about client id just validate the context
context.Validated();
}
The reason for this is that if you don't override ValidateClientAuthentication and validate the context, it is assumed as rejected and you'll always get that error.