unable to setup swagger UI in WEB API .net framework project - asp.net-web-api

i have SwaggerConfig Setup like this
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "My.API");
})
.EnableSwaggerUi(c =>
{
});
}
}
and WebApiConfig setup like this
public static class WebApiConfig
{
/// <summary>
/// Register Method for Configuration Settings.
/// </summary>
/// <returns>Returns a HttpConfiguration which is a complex object which represents a configuration of HttpServer.
/// </returns>
public static HttpConfiguration Register()
{
var config = new HttpConfiguration();
// Web API routes
config.MapHttpAttributeRoutes();
config.MessageHandlers.Add(new CancelledTaskBugWorkaroundMessageHandler());
// SetupOData(config);
// require authentication for all controllers
config.Filters.Add(new AuthorizeAttribute());
config.Filters.Add(new HostAuthenticationAttribute("Bearer"));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "GetPagedData",
routeTemplate: "{controller}/{pageNumber}/{pageSize}"
);
// Web API configuration and services
config.EnableCors();
// pulling out the XML for now -teg
config.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(
new MediaTypeHeaderValue("application/json-patch+json"));
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(
new MediaTypeHeaderValue("text/html"));
var logger = new LoggerConfiguration()
.ReadFrom.AppSettings()
.CreateLogger();
config.Services.Add(typeof(IExceptionLogger), new MedefisExceptionLogger(logger));
config.Filters.Add(new CommonExceptionFilterAttribute());
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Services.Replace(typeof(IDocumentationProvider),
new XmlCommentDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Medefis.API.XML")));
return config;
}
}
in the Startup.cs i am calling SwaggerConfig.Register() and few lines later i am also calling WebApiConfig.Register()
public class Startup
{
/// <summary>
/// Configuration for IAppBuilder on MedefisAPIUri
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
SwaggerConfig.Register();
var logger = new LoggerConfiguration()
.ReadFrom.AppSettings()
.CreateLogger();
logger.Information("Api Startup");
AutoMapperConfiguration.Configure();
//CreateFoldersOnFileSystem();
app.UseCors(CorsOptions.AllowAll);
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
AntiForgeryConfig.UniqueClaimTypeIdentifier = IdentityServer3.Core.Constants.ClaimTypes.Subject;
if (ConfigurationManager.AppSettings["UseIdentityServer"] == "True")
{
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = MedefisConstants.MedefisSTSConfig,
RequiredScopes = new[] { MedefisConstants.Scopes.api },
ClientId = MedefisConstants.Scopes.api,
ClientSecret = MedefisConstants.Secrets.InternalAPIClientSecret
}
);
}
// https://stackoverflow.com/questions/15637491/how-to-inject-httpcontextbase-using-autofac-in-asp-net-mvc-4
builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>().InstancePerRequest();
builder.RegisterType<ContactEmailRepository>().As<IContactEmailRepository>().InstancePerRequest();
builder.RegisterType<EmailMedefisUsersHelper>().As<IEmailMedefisUsersHelper>().InstancePerRequest();
builder.RegisterType<AgencyBiddingProfileService>().As<IAgencyBiddingProfileService>().InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<UploadDocumentHelper>().As<IUploadDocumentHelper>().InstancePerRequest();
builder.RegisterModule(new ValidationModule());
//Service
//builder.RegisterType<..... // su registerations goes here
//Setup
var config = WebApiConfig.Register();
builder.RegisterWebApiFilterProvider(config);
builder.RegisterInstance(config).As<HttpConfiguration>().SingleInstance().PropertiesAutowired();
var container = builder.Build();
config.EnableDependencyInjection();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.Use(typeof(WebApiAuthUserMiddleware));
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
config.EnsureInitialized();
}
}
the problem is that when i call http://localhost:xxxx/swagger it redirects to /swagger/ui/index but i get the error
No type was found that matches the controller named 'swagger'
please not the i am using Swashbuckle.Net45 version 5.2.1
Update
after prefixing the paths with "api/" in routeTemplate: "api/{controller}/{id}""
i was able to load the UI but none of paths and definitions are empty.

Solution was not to use SwaggerConfig.Register() separately instead do this configuration inside WebApiConfig.

Related

Authenticating an MVC application with tokens WebAPI

I have written a basic WebAPI that authenticates using Jwt Tokens. When I make a test API call using Postman I get a token issued successfully. I am however stuck in getting my MVC application to authenticate using the tokens.
Here is the controller for the API -
[HttpPost]
[Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel model)
{
var user = await userManager.FindByNameAsync(model.Username);
if (user != null && await userManager.CheckPasswordAsync(user, model.Password))
{
var authClaims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YVBy0OLlMQG6VVVp1OH7Xzyr7gHuw1qvUC5dcGt3SBM="));
var token = new JwtSecurityToken(
issuer: "https://localhost:44350",
audience: "https://localhost:44350",
expires: DateTime.Now.AddHours(3),
claims: authClaims,
signingCredentials: new Microsoft.IdentityModel.Tokens.SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
});
}
return Unauthorized();
}
Here is the Startup.cs for the WebAPI -
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "https://localhost:44350/",
ValidIssuer = "https://localhost:44350/",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("AA873344gshtrhjLJKJSLKF8u4o8grwieot4KJHFs9847GGSD"))
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
SeedDB.Initialize(app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope().ServiceProvider);
}
}
My code for the MVC applicaiton controller is below. It generates a token successfully, but I cant work out how to populate HttpContext.User.Identity?
[HttpPost]
public async Task<ActionResult> Index(LoginModel login)
{
string url = BaseUrl + "api/authenticate/login";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
var postTask = client.PostAsJsonAsync<LoginModel>("login", login);
postTask.Wait();
var result = postTask.Result;
if (result.IsSuccessStatusCode)
{
var user = HttpContext.User.Identity as ClaimsIdentity;
var tokenDetails = JsonConvert.DeserializeObject<Dictionary<string, string>>(result.Content.ReadAsStringAsync().Result);
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, login.Username, "string"),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.Ticks.ToString(), ClaimValueTypes.Integer64)
};
user.AddClaims(claims);
return RedirectToAction("Index", "Home", null);
}
}
ModelState.AddModelError(string.Empty, "Server error");
return View(login);
}
Here is the Startup.cs for the MVC application
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
You should add authentication service and use authentication middleware in the startup.cs:
services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(option =>
{
option.RequireHttpsMetadata = false;
option.SaveToken = true;
option.TokenValidationParameters = new TokenValidationParameters
{
RequireExpirationTime = true,
ValidateLifetime = true,
ValidIssuer = "Some Issuer",
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "Some Audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Security Key")),
ValidateIssuerSigningKey = true,
};
});
Also add app.UseAuthentication(); to the Configure method in startup.cs.

How to use Ninject to pass Initialized Instance into Web API Controller's Constructor

I am trying to use Ninject to inject dependency into a Self-host Web API Controller. I am success to inject the dependency as follows:
public static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<ITestAlert>().To<TestAlert>();
return kernel;
}
catch (Exception)
{
throw;
}
}
and
var server = WebApp.Start(baseAddress, (appBuilder) =>
{
// Configure Web API for self-host.
var config = new HttpConfiguration();
config.DependencyResolver = new NinjectResolver(NinjectConfig.CreateKernel());
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWebApi(config);
});
However, what I want to do is to pass an initialized instance from outside into the Controller's constructor. For instance, I have initialized an instance
public interface ITestAlert
{
string Fullname { get; set; }
}
and
public class TestAlert : ITestAlert
{
private string _fullname;
public string Fullname
{
get
{
return _fullname;
}
set
{
_fullname = value;
}
}
}
The instance is initialized as
public ITestAlert _testAlert;
_testAlert = new TestAlert
{
FullName = "Dummy"
};
What I expect is to get "Dummy" output string from the Get action method of the Controller,
private ITestAlert _testAlert = null;
public ValuesController(ITestAlert testAlert)
{
_testAlert = testAlert;
}
public string Get()
{
return _testAlert.Fullname;
}
However, I can only get a "null" return! Would you please advise how to pass the initialized instance into the Controller's constructor. Thanks.
Adding a dedicated binding should do the trick :
kernel.Bind<ITestAlert>()
.ToConstant(new TestAlert{FullName = Dummy"}).WhenInjectedInto<ValuesController>();

Dependency is not getting resolved in WebAPI (including OWIN) with Autofac

My global.asax looks like below
private void BuildIocContainer()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new BootstrapModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); //Set the WebApi DependencyResolver
}
protected void Application_Start()
{
BuildIocContainer();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I have built a bootstrap module for autofac like the one below
public class BootstrapModule : Autofac.Module
{
private Assembly AssemblyOf<T>()
{
return typeof(T).Assembly;
}
private Assembly WebAssembly
{
get { return Assembly.GetExecutingAssembly(); }
}
private void RegisterMvc(ContainerBuilder builder)
{
builder.RegisterType<AsyncControllerActionInvoker>()
.As<IActionInvoker>();
builder.RegisterControllers(WebAssembly)
.InjectActionInvoker();
builder.RegisterApiControllers(WebAssembly).InjectActionInvoker();
}
private void RegisterLogger(ContainerBuilder builder)
{
builder.Register(CreateLogger)
.SingleInstance();
builder.Register(_ => new NLogWrapper(LogManager.GetLogger("DefaultLogger")))
.As<Logging.ILogger>()
.SingleInstance();
}
private static System.Func<Type, Logging.ILogger> CreateLogger(IComponentContext componentContext)
{
return type => new NLogWrapper(LogManager.GetLogger(type.FullName));
}
protected override void Load(ContainerBuilder builder)
{
RegisterLogger(builder);
RegisterMvc(builder);
builder.RegisterAssemblyTypes(WebAssembly)
.AsImplementedInterfaces();
builder.RegisterType<UserService>()
.As<IUserService>()
.InstancePerRequest();
builder.RegisterAssemblyTypes(typeof(IUserService).Assembly)
.AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(IUserRepository).Assembly)
.AsImplementedInterfaces();
builder.RegisterFilterProvider();
}
}
Now, when I try to hit account controller through postman client,
private IUserService _userService;
public AccountController(IUserService userService)
{
_userService = userService;
}
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public HttpStatusCode Register(User model)
{
if (!ModelState.IsValid)
{
return HttpStatusCode.BadRequest;
}
// TODO : Write mapper if needed
var user = new ToolUser()
{
FirstName = model.FirstName,
LastName = model.LastName,
EmailID = model.EmailID,
DOB = Convert.ToDateTime(model.DateOfBirth),
Gender = model.Gender.ToString(),
TenandID = model.TenantID,
Password = model.Password
};
HttpStatusCode result = _userService.RegisterUser(user);
return result;
}
I get the error
"An error occurred when trying to create a controller of type 'AccountController'. Make sure that the controller has a parameterless public constructor."
What am I doing wrong?
You should have something like
HttpConfiguration config = new HttpConfiguration();
somewhere, which you use to register your routes etc.
Pass that config to your BuildIocContainer() mehod and add the line
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
instead of
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); //Set the WebApi DependencyResolver
Hope that helps

How to host web api in memory?

I have web api controller and i want to perform an integration tests. So I followed the article here to configured the in-memory Web API host.
My integration test and web api are two different projects in same VS solution.
Below is the code
Web API Controller
public class DocumentController : ApiController
{
public DocumentController(IDomainService domainService)
{
_domainService = domainService;
}
[HttpPost]
public async Task<IEnumerable<string>> Update([FromBody]IEnumerable<Document> request)
{
return await _domainService.Update(request).ConfigureAwait(false);
}
}
Integration Test
[TestClass]
public class IntegrationTests
{
private HttpServer _server;
private string _url = "http://www.strathweb.com/";
[TestInitialize]
public void Init()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(name: "Default", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional });
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.MessageHandlers.Add()
_server = new HttpServer(config);
}
[TestMethod]
public void UpdateTransformedDocuments()
{
var doc = new Document()
{
// set all properties
}
var client = new HttpClient(_server);
var request = createRequest<Document>("api/document/Update", "application/json", HttpMethod.Post, doc, new JsonMediaTypeFormatter());
using (var response = client.SendAsync(request).Result)
{
// do something with response here
}
}
private HttpRequestMessage createRequest<T>(string url, string mthv, HttpMethod method, T content, MediaTypeFormatter formatter) where T : class
{
Create HttpRequestMessage here
}
}
However im getting error
StatusCode: 404, ReasonPhrase: 'Not Found'
How & where do I tell the HttpServer to execute DocumentController?
Update1
So I fixed above error by changing the [TestIntialize] code as below
[TestInitialize]
public void Init()
{
var config = new HttpConfiguration();
UnityWebApiActivator.Start();
WebApiConfig.Register(config);
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
_server = new HttpServer(config);
}
I don't get 404 error now. However Unity is not able to resolve DocumentController. The HttpResponse contains error
An error occurred when trying to create a controller of type
'DocumentController'. Make sure that the controller has a
parameterless public constructor.
In TestInitialize method I'm calling UnityWebApiActivator.Start() which registers all the require types with Unity.
i resolved my 2nd issue by setting 'HttpConfiguration.DependencyResolver'
[TestInitialize]
public void Init()
{
var config = new HttpConfiguration();
//UnityWebApiActivator.Start();
config.DependencyResolver = new UnityHierarchicalDependencyResolver(UnityConfig.GetConfiguredContainer());
WebApiConfig.Register(config);
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
_server = new HttpServer(config);
}

Web API 2 OWIN Bearer token authentication - AccessTokenFormat null?

I have an existing ASP.NET MVC 5 project and I'm adding a Web API 2 project to it. I want to use bearer token authentication and have followed Hongye Sun's tutorial "OWIN Bearer Token Authentication with Web API Sample" and this question as well.
In my Login method, for the line Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);, the AccessTokenFormat is null. Any idea why?
My AccountController:
[RoutePrefix("api")]
public class AccountController : ApiController
{
public AccountController() {}
// POST api/login
[HttpPost]
[Route("login")]
public HttpResponseMessage Login(int id, string pwd)
{
if (id > 0) // testing - not authenticating right now
{
var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, id.ToString()));
AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
var token = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ObjectContent<object>(new
{
UserName = id.ToString(),
AccessToken = token
}, Configuration.Formatters.JsonFormatter)
};
}
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
// POST api/token
[Route("token")]
[HttpPost]
public HttpResponseMessage Token(int id, string pwd)
{
// Never reaches here. Do I need this method?
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
Startup class:
public class Startup
{
private static readonly ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static Func<MyUserManager> UserManagerFactory { get; set; }
public static string PublicClientId { get; private set; }
static Startup()
{
PublicClientId = "MyWeb";
UserManagerFactory = () => new MyUserManager(new UserStore<MyIdentityUser>());
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/token"),
Provider = new MyWebOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/login"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
public void Configuration(IAppBuilder app)
{
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/api/login")
});
// Configure Web API to use only bearer token authentication.
var config = GlobalConfiguration.Configuration;
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthBearerOptions.AuthenticationType));
app.UseWebApi(config);
}
}
MyIdentityUser just adds an extra property:
public class MyIdentityUser : IdentityUser
{
public int SecurityLevel { get; set; }
}
MyUserManager calls my custom user authentication method to an internal server:
public class MyUserManager : UserManager<MyIdentityUser>
{
public MyUserManager(IUserStore<MyIdentityUser> store) : base(store) { }
public MyIdentityUser ValidateUser(int id, string pwd)
{
LoginIdentityUser user = null;
if (MyApplication.ValidateUser(id, pwd))
{
// user = ??? - not yet implemented
}
return user;
}
}
MyWebOAuthProvider (I took this from the SPA template. Only GrantResourceOwnerCredentials has been changed):
public class MyWebOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
private readonly Func<MyUserManager> _userManagerFactory;
public MyWebOAuthProvider(string publicClientId, Func<MyUserManager> userManagerFactory)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
if (userManagerFactory == null)
{
throw new ArgumentNullException("userManagerFactory");
}
_publicClientId = publicClientId;
_userManagerFactory = userManagerFactory;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (MyUserManager userManager = _userManagerFactory())
{
MyIdentityUser user = null;
var ctx = context as MyWebOAuthGrantResourceOwnerCredentialsContext;
if (ctx != null)
{
user = userManager.ValidateUser(ctx.Id, ctx.Pwd);
}
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
... // unchanged from SPA template
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
... // unchanged from SPA template
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
... // unchanged from SPA template
}
public static AuthenticationProperties CreateProperties(string userName)
{
... // unchanged from SPA template
}
}
MyWebOAuthGrantResourceOwnerCredientialsContext:
public class MyWebOAuthGrantResourceOwnerCredentialsContext : OAuthGrantResourceOwnerCredentialsContext
{
public MyWebOAuthGrantResourceOwnerCredentialsContext (IOwinContext context, OAuthAuthorizationServerOptions options, string clientId, string userName, string password, IList<string> scope)
: base(context, options, clientId, userName, password, scope)
{ }
public int Id { get; set; }
public string Pwd { get; set; }
}
How is AccessTokenFormat set? Is what I've set up correct? I'm not authenticating against any external services, just a legacy internal server.
Thanks.
I had the same problem - it was to do with my initialisation in Startup().
Like you, I was storing the OAuthBearerOptions in a static field:
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
But then I was wrongly using a new instance of the same class later on:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); // wrong!
Obviously the fix was to use the static field instead:
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
In fact, it doesn't look like you call UseOAuthBearerAuthentication() at all. I followed this excellent series of posts by Taiseer Joudeh.
Full Startup.cs:
public class Startup
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
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)
{
//use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() {
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider() // see post
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
//[Configure External Logins...]
}
}
I'm not sure if you're still looking for the answer to this - but here's a bit of code that I'm using in my AngularJS app to get the security token from my WebAPI2 endpoint.
$http({
method: 'POST', url: '/token', data: { username: uName, password: uPassword, grant_type: 'password' },
transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
}
}).success(function (data, status, headers, config) {
console.log("http success", data);
accessToken.value = data.access_token;
console.log("access token = ", accessToken.value);
}).error(function (data, status, headers, config) {
console.log("http error", data);
});
I can then pass the accessToken in the header of any other requests in order to get the authentication validation.
I have removed the sample code as it can cause confusion when it's used with Web API and SPA template. You'd better stay with the template code to use OAuth authorization server to generate token. In your scenario, you should use resource owner password grant to authenticate the user. Please check my blog on SPA template which has details about the password flow on http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx
Instead of writing your own Web API to handle login, you need to use OWIN OAuth Server's /token endpoint to handle password login.

Resources