I am developing a web application, mobile application and desktop application which all can access the data with the help of a single API which can be developed by ASP.NET Web API.
In my Web API can I authenticate the user credentials and the consumer Application key with the help of OAuth?
Can you guys guide me to achieve the same with any examples?
Add the following lines in startup class
public class Startup
{
public void Configuration(IAppBuilder app)
{
var oauthProvider = new OAuthAuthorizationServerProvider
{
OnGrantResourceOwnerCredentials = async context =>
{
IsValid = true;
//You can get the username and password by context.username and context.password
if (IsValid)
{
var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
claimsIdentity.AddClaim(new Claim("user", context.UserName));
context.Validated(claimsIdentity);
return;
}
context.Rejected();
},
OnValidateClientAuthentication = async context =>
{
string clientId;
string clientSecret;
if (context.TryGetBasicCredentials(out clientId, out clientSecret))
{
if (clientId == GlobalAppSettings.SystemSettings.ApplicationKey)
{
context.Validated();
}
}
}
};
var oauthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/accesstoken"),
Provider = oauthProvider,
AuthorizationCodeExpireTimeSpan = TimeSpan.FromMinutes(1),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(3),
SystemClock = new SystemClock()
};
app.UseOAuthAuthorizationServer(oauthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
}
}
And invoke the startup method by start up the new OAuth APP from where you need the authorization and OAuth token
You can use your own host by using the self hosting namespace.
Otherwise you can use Microsoft.Owin.Host.HttpListener namespace and host the OAuth app in different host as below
var Basesite = "http://localhost:9327/";
var homeProcessorModel = new HomeProcessorModel();
using (WebApp.Start<Startup>(url: Basesite))
{
var client = new HttpClient();
var form = new Dictionary<string, string>
{
{"grant_type","password"},
{"userName",username},
{"passWord",password}
};//If you are using grant_type as password, you have to send the username and password to OAuth protocol.
var tokenResponse = client.PostAsync(Basesite + "accesstoken", new FormUrlEncodedContent(form)).Result;
var token = tokenResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() }).Result;
//You can get the token with token.AccessToken object
}
Related
I've been trying an OpenID authorization code flow using openiddict. Both the OIDC client and server are implemented using OpenIddict (with Owin) and both are web forms apps hosted in IIS.
The client side config:
public bool SetUp(IAppBuilder app)
{
try
{
var container = CreateContainer();
app.UseAutofacLifetimeScopeInjector(container);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieHttpOnly = true,
CookieSecure = CookieSecureOption.SameAsRequest
});
app.UseMiddlewareFromContainer<OpenIddictClientOwinMiddleware>();
DependencyResolver.SetResolver(new
AutofacDependencyResolver(container));
var scope = container.BeginLifetimeScope();
return true;
}
catch (Exception ex)
{
_logger.Error("OidcController: SetUp", ex);
return false;
}
}
private IContainer CreateContainer()
{
IdentityModelEventSource.ShowPII = true;
var services = new ServiceCollection();
services
.AddOpenIddict()
.AddCore(options => { })
.AddClient(options => {
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate()
.UseOwin()
.EnableRedirectionEndpointPassthrough();
options.UseSystemNetHttp();
//Try to reuse key cloak options
options.AddRegistration(new OpenIddictClientRegistration
{
Issuer = new Uri(_oidcSettings.ExternalUrl.RemoveAuth(), UriKind.Absolute),
ClientId = _oidcSettings.ClientId,
ClientSecret = _oidcSettings.ClientSecret,
RedirectUri = <the redirect uri>,
Scopes = { "email", "profile", "offline_access" }
});
});
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterControllers(typeof(OidcController).Assembly);
return builder.Build();
}
The server config:
protected void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var services = new ServiceCollection();
services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
options.UseEntityFramework()
.UseDbContext<ApplicationDbContext>();
})
// Register the OpenIddict server components.
.AddServer(options =>
{
// Enable the authorization and token endpoints.
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetTokenEndpointUris("/connect/token");
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles);
options.AllowAuthorizationCodeFlow();
// Register the signing and encryption credentials.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
options.UseOwin()
.EnableAuthorizationEndpointPassthrough();
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
options.UseLocalServer();
// Register the OWIN host.
options.UseOwin();
});
// Create a new Autofac container and import the OpenIddict services.
var builder = new ContainerBuilder();
builder.Populate(services);
Provider = new ContainerProvider(builder.Build());
Task.Run(async delegate
{
using var scope = Provider.ApplicationContainer.BeginLifetimeScope();
var context = scope.Resolve<ApplicationDbContext>();
context.Database.CreateIfNotExists();
var manager = scope.Resolve<IOpenIddictApplicationManager>();
//Add the client app
if (await manager.FindByClientIdAsync("SAS-Console") == null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "SAS-Console",
ClientSecret = "413ca0fd-8e24-4362-aaa3-d74095227658",
ConsentType = ConsentTypes.Implicit,
DisplayName = "Console application",
RedirectUris =
{
<client app redirect url>
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.ResponseTypes.Code
}
});
}
}).GetAwaiter().GetResult();
}
But when I run both, I continue to get an error:
IDX20803: Unable to obtain configuration from: 'https:///.well-known/openid-configuration
I have implemented the code for generating access token.I am able to get the token from default Token End Point Path(https://localhost:44312/token) when trying to get it from Postman.
However I want to implement Login Controller which internally should call the default token end point and send the response.
public async Task<IHttpActionResult> Login(UserModel userModel)
{
HttpResponseMessage response;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44312/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("cache-control", "no-cache");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
userModel.grant_type = "password";
var parameters = new Dictionary<string, string> { { "username", userModel.UserName }, { "password", userModel.Password }, { "grant_type", "password" } };
var encodedContent = new FormUrlEncodedContent(parameters);
response = client.PostAsync("/token", encodedContent).Result;
}
return Ok(response);
}
I get an httpstatus OK response but not the access token. Any guidance in solving the issue would be helpful.
I am tying to do JwtBearerAuthentication on my .net WebAPI and it is just not working. The Authorize attribute is always claiming isAuthorized = false.
I am working with Okta as the SSO. I am authenticating on my client side and getting both an access token and id token. On a webapi get request I am providing the access token (i have also tried the id token) in the authorize header and I am able to see the authorize header with the token in the webapi actioncontext.
In my startup.cs I have the following
var clientID = WebConfigurationManager.AppSettings["okta:ClientId"];
var oidcIssuer = WebConfigurationManager.AppSettings["okta:OIDC_Issuer"];
TokenValidationParameters tvps = new TokenValidationParameters
{
ValidAudience = clientID,
ValidateAudience = true,
ValidIssuer = oidcIssuer,
ValidateIssuer = true
};
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
TokenValidationParameters = tvps,
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new OpenIdConnectCachingSecurityTokenProvider(oidcIssuer + "/.well-known/openid-configuration")
}
});
Am i missing some TokenValidationParameters that I need?
As I was typing this, I see that twaldron was able to figure it out!
I also realized that he was asking about WebAPI, and not MVC. However, here is the code that I needed to get the following working with ASP.NET Core MVC, of particular interest might be this line, which is necessary to get access to the additional claims in the JWT:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
Here is how this code sample works from the command line, the $ID_TOKEN variable contains a valid JWT:
$ curl -H "Authorization: Bearer ${ID_TOKEN}" http://localhost:3000/test/test
sub: 01a23b4cd5eFgHI6j7k8 email:test#example.com
Setup.cs:
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
namespace WebApplication
{
public class Startup
{
readonly string clientId = string.Empty;
readonly string issuer = string.Empty;
readonly string audience = string.Empty;
public Startup(IHostingEnvironment env)
{
clientId = "A0b1CDef2GHIj3k4lm5n";
issuer = "https://example.okta.com";
audience = "A0b1CDef2GHIj3k4lm5n";
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddDebug();
// https://github.com/aspnet/Security/issues/1043#issuecomment-261937401
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
TokenValidationParameters tvps = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = audience,
ValidateIssuer = true,
ValidIssuer = issuer,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
MetadataAddress = issuer + "/.well-known/openid-configuration",
TokenValidationParameters = tvps
});
app.UseStaticFiles();
// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvc(routes =>
{
routes.MapRoute(
name: "test-controller",
template: "test/{action}",
defaults: new { controller = "Test", action = "Index" }
);
routes.MapRoute(
name: "default",
template: "{controller=Test}/{action=Index}/{id?}");
});
}
}
}
In Controllers/Test.cs:
[Authorize]
public IActionResult Test()
{
var contextUser = User.Identity as ClaimsIdentity;
Dictionary<string, string> claim = contextUser.Claims.ToDictionary(x => x.Type, x => x.Value);
var output = "sub: " + claim["sub"] + " email:" + claim["email"];
return Content(output);
}
My problem was not with the options.
It was 100% the need to move
app.UseWebApi(config);
below all the owin setup stuff.
I am having some trouble accessing my userId after and creating my dictionary with connectionIds. I currently and using OAuthBearer for authentication of my users and saving information into ClaimsIdentity, something like below:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
},
{
"userId", user.Id
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
}
When I am in my startup/my signalR hub, when I try using the IRequest.user.identity.name or HttpContext.Current.User.Identity.GetUserId();, I get an empty value.
public string GetUserId(IRequest request)
{
var userId = request.User.Identity.Name;
return userId.ToString();
}
I'm thinking that something is wrong with my claims because I can't access the values of UserId and userName. If I run HttpContext.Current.User.Identity.GetUserId(); anywhere else, I can get the userId.
Any help would be greatly appreciated as I've been stuck on this for quite a while.
I had the same issue, try replacing HttpContext.Current with
Context.Request.GetHttpContext() inside your signalR hub methods.
According to MSDN the HubCallerContext returns the context of the client.
I have setup an ASP.NET WebApi project with support of bearer token like this:
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
var oAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(oAuthBearerOptions);
When I make a request to the token endpoint I have this answer
{
"access_token":"GST9UwSuesiYhkezr94K4xwuzvNQ",
"token_type":"bearer",
"expires_in":86399
}
Is there any way to enrich the token with additional fields like this?
{
"access_token":"GST9UwSuesiYhkezr94K4xwuzvNQ",
"token_type":"bearer",
"expires_in":86399,
"username":"user#mail.com"
}
Well here is the solution:
In your class that inherits from OAuthAuthorizationServerProvider
public override async Task TokenEndpoint(OAuthTokenEndpointContext context)
{
context.AdditionalResponseParameters.Add("username", "user#mail.com");
return Task.FromResult<object>(null);
}