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.
Related
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'm using signalR and I'm trying to get the [Authorize] attribute to work. With below setup I get 401 Unauthorized..
Right before I start the Hub I set the Authorization like this:
$.signalR.ajaxDefaults.headers = { Authorization: 'Bearer ' + settingsService.getItem('authData').token }
I can see that the header is sent in the request like below:
Authorization: Bearer F0wGNa7cAwUjOFI27TDR_w7N4Ncmz66PGpsU1AH2AWt0Gdt39e2o4DGwPsBXTAlIwHrAF-YHE9I1KGLxfabE0QxpcY5mLn1gxGWStOSX_W5NaUQlRlpRu5k-s6YLH-vVjlakqap_YXbzPelZJOjwcz7Ea5VHcCUFQ5xDYYK0VJXDIqMwQXZPIyVtNVu1RyLLVj7iOZaMd-41gHKWNqFWJBmK5WkWw06dI4AWiifWJT_8v1WrFPCAzYfiT0U
My Hub:
[Authorize]
[HubName("myHub")]
public class DataHub : Hub {
private static bool _isInitated;
public DataHub() {
//Do stuff
}
}
}
The token is created like this:
var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim("Username", "myname"));
var properties = new AuthenticationProperties() {
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(Startup.OAuthOptions.AccessTokenExpireTimeSpan)
};
var ticket = new AuthenticationTicket(identity, properties);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
var response = new JObject(new JProperty("access_token", accessToken), new JProperty("expires_in", Startup.OAuthOptions.AccessTokenExpireTimeSpan.TotalMinutes));
return Ok(new { token = response });
Am I missing something special here? Don't really get it.
If this is an issue for someone else I got the answer here. In my startup.cs I had to move app.MapSignalR(); last in the Configuration method. Like below:
public class Startup {
public void Configuration(IAppBuilder app) {
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
GlobalHost.Configuration.DefaultMessageBufferSize = 100;
ConfigureOAuth(app);
app.MapSignalR();
}
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public void ConfigureOAuth(IAppBuilder app) {
OAuthOptions = new OAuthAuthorizationServerOptions {
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(7)
};
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
Before I had ConfigureOAuth(app); last.. and then it didn't work
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'm able to successfully retrieve token, but when trying to authenticate using the token I always get the Authorization has been denied for this request message.
My Startup.cs file contains the following methods
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
ConfigureOAuth(app);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter().First();
jsonFormatter.SerializerSettings
.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
private void ConfigureOAuth(IAppBuilder app)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new DefaultAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
The DefaultAuthorizationServerProvider.cs class contains the following
public class DefaultAuthorizationServerProvider : 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[] { "*" });
var identityManager = new IdentityManager();
var identity = identityManager.Get(context.UserName, context.Password,
new IpAddressProvider().Provide(IpAddressType.Forwarding));
if (identity == null)
{
context.SetError("invalid_grant", "Authentication failed. Please make sure you provided the correct username and password.");
}
else
{
identity.AddClaim(new Claim(ClaimTypes.Role, "User"));
context.Validated(identity);
}
}
}
And the IdentityManager.cs class have the following
public class IdentityManager : IIdentityManager
{
public virtual ClaimsIdentity Get
(
string username,
string password,
string ipAddress
)
{
var authenticateUserWorkflowOutput = new AuthenticateUserWorkflowHelper().Execute
(
new AuthenticateUserWorkflowInput
{
Username = username,
Password = password,
IpAddress = ipAddress
},
new AuthenticateUserWorkflowState()
);
if (authenticateUserWorkflowOutput.Message.Exception != null)
{
return null;
}
if (!authenticateUserWorkflowOutput.Authenticated)
{
return null;
}
return authenticateUserWorkflowOutput.User != null ? new Infrastructure.Identity(new[]
{
new Claim(ClaimTypes.Name, authenticateUserWorkflowOutput.MasterUser.EmailAddress),
}, "ApplicationCookie") : null;
}
}
Using Fiddler I can successfully retrieve a token
But when I try to authenticate using the token I get the following response
Ok, I found the problem in my Startup class. I was missing the following
[assembly: OwinStartup(typeof(Yugasat.System.ServiceLayer.Startup))]
namespace Yugasat.System.ServiceLayer
and the ConfigureOAuth(app);call needed to be moved to the top of the Configuration method. Below is my new Startup.cs class.
[assembly: OwinStartup(typeof(Yugasat.System.ServiceLayer.Startup))]
namespace Yugasat.System.ServiceLayer
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
private void ConfigureOAuth(IAppBuilder app)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new DefaultAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}