c# web api owin authentication - c#

I am very new to WebAPi Authentication even it seems OWIN being popular use. I dont understand why I should use EntityFramework for OWIN authentication as ApplicationDbContext is inhreting from IdentityDbContext and IdentityDbContext is in EntityFramework namespace. Below is procedure which is created automatically when we choose Individual User Accounts within WebApi project template:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
}
Within ConfigureAuth procedure ApplicationDbContext is referenced.
Could you pls help me to write simple Authentication with OWIN and not to use EntityFramework?
Thanks.

you donĀ“t need to use EF, yes, the template uses EF and ASPNET Identity to do the authentication, but you can start using the black template and add it without EF, look the following part of code:
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
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.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
SimpleAuthorizationServerProvider.cs
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (context.UserName != "Admin")
{
context.SetError("upps!", "Wrong data");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
Also, you can download a simple example here: http://1drv.ms/1mmaqtn
Regards,

Related

How to send bearer token to views in ASP NET MVC 5?

I have a .NET MVC and WEB API project. I want to call the WEB API controllers from javascript but I didn't find a way to send the token to my views. I want to add the bearer token in Viewbag variable, using the below code:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//GetBearerToken doesn't exists
ViewBag.BearerToken = Request.GetOwinContext().GetBearerToken();
}
In _Layout.cshtml, I added the folowing code to set Authorization header for all ajax requests:
<script type="text/javascript">
window.token = '#Viewbag.BearerToken';
// Add Authorization header on ajax requests
if(window.token) {
(function setAjaxRequestsAuthorizationHeader(token) {
$.ajaxPrefilter(function onAjaxPrefilter(options) {
if (!options.beforeSend) {
options.beforeSend = function onBeforeSend(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
}
}
});
}(window.token));
window.token = null;
}
</script>
Below is my Startup configuration method:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
//app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext(AppDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: id => id.GetUserId<int>())
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
To get a token I also tried (in the MVC Controller methods):
var token = new ClaimsPrincipal(User.Identity).Claims.FirstOrDefault(x => x.Type == "access_token")?.Value;
But the value of variable token is always null.
I have also inspected all claims values but I didn't find any possible token.
How can I send the token to controller views?
Solved
I solved the problem by doing the following steps:
In Startup.Auth.cs I added the folowing static property:
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
I have created an extension method for ApplicationUser class:
public static async Task<ClaimsIdentity> GenerateUserIdentityAsync(this ApplicationUser user, UserManager<ApplicationUser, int> manager, string type)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Genereate Bearer token
if (manager is ApplicationUserManager && type == DefaultAuthenticationTypes.ApplicationCookie)
{
var accessTokenFormat = Startup.OAuthOptions.AccessTokenFormat;
if (accessTokenFormat != null)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
["userName"] = user.UserName
};
AuthenticationProperties properties = new AuthenticationProperties(data);
AuthenticationTicket ticket = new AuthenticationTicket(userIdentity, properties);
var token = accessTokenFormat.Protect(ticket);
userIdentity.AddClaim(new Claim("Access_Token", token));
}
}
return userIdentity;
}
I modified the method OnActionExecuting to send token to the client:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Request.IsAuthenticated)
{
var identity = (ClaimsIdentity)User.Identity;
ViewBag.BearerToken = identity.Claims.FirstOrDefault(x => x.Type == "Access_Token")?.Value;
}
}
In Startup.Auth.cs use the extension method created at step 2 when creating Identity:
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager,
DefaultAuthenticationTypes.ApplicationCookie),
getUserIdCallback: id => id.GetUserId<int>())

Cache user profile with external authentication .NET Core IdentityServer4

New to the whole Identity concept but I've had a couple of Google searches and haven't found a reply I felt fitting.
I'm using .NET Core 1.0.0 with EF Core and IdentityServer 4 (ID4).
The ID4 is on a separate server and the only information I get in the client is the claims. I'd like to have access to the full (extended) user profile, preferrably from User.Identity.
So how to I set up so that the User.Identity is populated with all the properties on the ApplicationUser model without sending a DB request every time? I'd like the information to be stored in cache on authentication until the session ends.
What I don't want to do is that in each controller set up a query to get the additional information. All controllers on the client will be inheriting from a base controller, meaning I could DI some service if that's necessary.
Thanks in advance.
Client
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = Configuration.GetSection("IdentityServer").GetValue<string>("Authority"),
RequireHttpsMetadata = false,
ClientId = "RateAdminApp"
});
ID4
app.UseIdentity();
app.UseIdentityServer();
services.AddDeveloperIdentityServer()
.AddOperationalStore(builder => builder.UseSqlServer("Server=localhost;Database=Identities;MultipleActiveResultSets=true;Integrated Security=true", options => options.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name)))
.AddConfigurationStore(builder => builder.UseSqlServer("Server=localhost;Database=Identities;MultipleActiveResultSets=true;Integrated Security=true", options => options.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name)))
.AddAspNetIdentity<ApplicationUser>();
ApplicationUser Model
public class ApplicationUser : IdentityUser
{
[Column(TypeName = "varchar(100)")]
public string FirstName { get; set; }
[Column(TypeName = "varchar(100)")]
public string LastName { get; set; }
[Column(TypeName = "nvarchar(max)")]
public string ProfilePictureBase64 { get; set; }
}
If you want to transform claims on the identity server, for your case(you use aspnet identity) overriding UserClaimsPrincipalFactory is a solution(see Store data in cookie with asp.net core identity).
public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
public AppClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
{
}
public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
var principal = await base.CreateAsync(user);
((ClaimsIdentity)principal.Identity).AddClaims(new[] {
new Claim("FirstName", user.FirstName)
});
return principal;
}
}
// register it
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();
Also you can use events(on the client application) to add extra claims into cookie, it provides claims until the user log out.
There are two(maybe more than) options:
First using OnTicketReceived of openidconnect authentication:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = Configuration.GetSection("IdentityServer").GetValue<string>("Authority"),
RequireHttpsMetadata = false,
ClientId = "RateAdminApp",
Events = new OpenIdConnectEvents
{
OnTicketReceived = e =>
{
// get claims from user profile
// add claims into e.Ticket.Principal
e.Ticket = new AuthenticationTicket(e.Ticket.Principal, e.Ticket.Properties, e.Ticket.AuthenticationScheme);
return Task.CompletedTask;
}
}
});
Or using OnSigningIn event of cookie authentication
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
Events = new CookieAuthenticationEvents()
{
OnSigningIn = async (context) =>
{
ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
// get claims from user profile
// add these claims into identity
}
}
});
See similar question for solution on the client application: Transforming Open Id Connect claims in ASP.Net Core

IOwinContext.Get<DbContext>() returns null

I am trying to add authentication to add authentication to an existing MVC 5 application I started from an empty project. I started a new WebAPI project with individual user accounts so I could see how it was configured. I copied over the code having to do with authentication and refactored the namespaces and class names. In the code below, the first line var identityContext = context.Get<IdentityDbContext>() returns null and causes the second line var userStore = new UserStore<AdminAppUser>(identityContext) to throw an error due to a null parameter.
I probably didn't include enough code, as I am very new to MVC Authentication and have a poor understanding of how all the pieces fit together. if I need to include more code please let me know which pieces would be useful. Thank you!
public static AdminAppUserManager Create(IdentityFactoryOptions<AdminAppUserManager> options, IOwinContext context)
{
var identityContext = context.Get<IdentityDbContext>();
var userStore = new UserStore<AdminAppUser>(identityContext);
var manager = new AdminAppUserManager(userStore);
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<AdminAppUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<AdminAppUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
EDIT:
startup.auth.cs
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(AdminAppIdentityDbContext.Create);
app.CreatePerOwinContext<AdminAppUserManager>(AdminAppUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
}
startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
Edit 2:
public class AdminAppIdentityDbContext : IdentityDbContext<AdminAppUser>
{
public AdminAppIdentityDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static AdminAppIdentityDbContext Create()
{
return new AdminAppIdentityDbContext();
}
}
There should be some sort of ConfigureAuth method that gets called at startup to establish that there is one IdentityDbContext per Owin context. That call will look like:
app.CreatePerOwinContext(IdentityDbContext.Create);
That call should be in the boilerplate that VS generates automatically for you.
You could also just replace
var identityContext = context.Get<IdentityDbContext> with
var identityContext = new AdminAppIdentityDbContext();,
it doesn't really matter. It may spare your time.

.Net OWIN WebApiConfig not being called

I created a blank project so I could create an angular application.
Now I have all that in place, I decided that I want to add Web API to this project. I installed all the required packages and set up the WebApiConfig.cs file.
Then I installed OWIN and created the OWIN Startup Class. When I run my project, the OWIN Startup Class is invoked properly, but the WebApiConfig is not.
In the past (pre-OWIN) using Global.asax was how you fired all your configuration classes, but because I am using OWIN the global.asax file is not needed and therefore I never created it.
Has someone come across this before and knows what I am doing wrong?
Update 1
I added a Global.asax page and it executed.
I was under the impression that if you use OWIN, you should delete your Global.asax file?
Here are both the Global.asax file
public class Global : HttpApplication
{
protected void Application_Start()
{
// Add these two lines to initialize Routes and Filters:
WebApiConfig.Register(GlobalConfiguration.Configuration);
}
}
and the Startup.Config file.
public class StartupConfig
{
public static UserService<User> UserService { get; set; }
public static string PublicClientId { get; private set; }
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
static StartupConfig()
{
UserService = new UserService<User>(new UnitOfWork<DatabaseContext>(), false, true);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new OAuthProvider<User>(PublicClientId, UserService),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void Configuration(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "vnaJZLYwWFbv7GBlDeMbfwAlD",
// consumerSecret: "Q1FE1hEN6prXnK2O9TYihTFyOQmcQmrZJses0rT8Au4OsDQISQ");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication();
}
}
Update 2
My startup class looks like this now:
public class StartupConfig
{
public static UserService<User> UserService { get; set; }
public static string PublicClientId { get; private set; }
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
static StartupConfig()
{
UserService = new UserService<User>(new UnitOfWork<DatabaseContext>(), false, true);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new OAuthProvider<User>(PublicClientId, UserService),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void Configuration(IAppBuilder app)
{
//var config = new HttpConfiguration();
//// Set up our configuration
//WebApiConfig.Register(config);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "vnaJZLYwWFbv7GBlDeMbfwAlD",
// consumerSecret: "Q1FE1hEN6prXnK2O9TYihTFyOQmcQmrZJses0rT8Au4OsDQISQ");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication();
}
}
If I uncomment the WebApiConfig line then the startup class is never executed.
Any idea why?
You'll need to call app.UseWebApi in your startup class, passing in the configuration you want to use. You'll also need to call your WebApiConfig's Register method there too. An example of how this might look in a cut down application is:
You could have an OWIN startup class that looks something like this:
// Tell OWIN to start with this
[assembly: OwinStartup(typeof(MyWebApi.Startup))]
namespace MyWebApi
{
public class Startup
{
/// <summary>
/// This method gets called automatically by OWIN when the application starts, it will pass in the IAppBuilder instance.
/// The WebApi is registered here and one of the built in shortcuts for using the WebApi is called to initialise it.
/// </summary>
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
}
The HttpConfiguration is created and passed to the WebApiConfig.Register method. We then use the app.UseWebApi(config) method to setup the web api. This is a helper method in System.Web.Http.Owin, you can get it by including the NuGet package Microsoft ASP.NET Web API 2.2 OWIN
The WebApiConfig class would look something like this:
namespace MyWebApi
{
public static class WebApiConfig
{
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 }
);
}
}
}
Certainly, If you use Owin you may delete you Global.asax file.
In your Owin Startup.cs you have to put your WebApiConfig registration.
public class Startup
{
public void Configuration(IAppBuilder app)
{
...
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
config.Filters.Add(new WebApiAuthorizeAttribute());
...
}
...
}

Owin Bearer Token Not Working for WebApi

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.

Categories

Resources