I am trying a token tutorial with web-api and I'm a little confuse, I can't figure how to get my Token, according to the tutorial the token comes from endpoint of Web Api, but I dont understand what is the specific function that produce the token or how to call it so I will get a token back to GetToken(), here some code:
class Startup
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()); app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
string PublicClientId = "self";
OAuthAuthorizationServerOptions OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new PureApi.Provider.ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Login"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
app.UseOAuthBearerTokens(OAuthOptions);
}
my LoginController
static string GetToken(string userName, string password)
{ var pairs = new List<KeyValuePair<string, string>>
{new KeyValuePair<string, string>( "grant_type", "password" ),
new KeyValuePair<string, string>( "username",userName ),
new KeyValuePair<string, string> ( "Password", password ) };
var content = new FormUrlEncodedContent(pairs);
using (var client = new HttpClient())
{
var response = client.PostAsync("http://localhost:2188/api/Test",content).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
class Provider
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{ foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{ context.AdditionalResponseParameters.Add(property.Key,property.Value);
}
return Task.FromResult<object>(null);
}
Related
I'm building this app and so far I've implemented the Identity API.
Login/Register works, which both return a user with a token. Using [Authorize] on my controllers works when I pass the JWT token in Postman.
However, when adding [Authorize(Roles = "Administrator")], I get a 403 Response, despite checking that the role "Administrator" is indeed assigned to the user.
This is the method in the UserController:
`[Authorize(Roles = ("Administrator"))]
[HttpGet]
public async Task<ActionResult<IEnumerable<AppUser>>> GetUsers()
{
return await _userManager.Users.ToListAsync();
}`
And this is the TokenService class:
`public class TokenService
{
public readonly IConfiguration _config;
public TokenService(IConfiguration config)
{
_config = config;
}
public string CreateToken(AppUser user)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Email, user.Email)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["TokenKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}`
And this is the Program.cs class
`private static async Task Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(opt =>
{
// Every endpoint requires Authentication, except for those that we add [AllowAnonnymous] to
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
opt.Filters.Add(new AuthorizeFilter(policy));
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<ApplicationDbContext>(option =>
option.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddControllersWithViews();
builder.Services.AddScoped<ICompany, CompanyService>();
builder.Services.AddScoped<IJobPosition, JobPositionService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IApplicationService, ApplicationService>();
builder.Services.AddScoped<IGetUser, UserService>();
builder.Services.AddScoped<IDocuments, DocumentService>();
builder.Services.AddScoped<IResults, ResultService>();
builder.Services.AddControllersWithViews();
builder.Services.AddDefaultIdentity<AppUser>().AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddIdentityServices(builder.Configuration);
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("Administrator"));
});
var app = builder.Build();
var scopeFactory = app.Services.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<ApplicationDbContext>();
var userManager = services.GetRequiredService<UserManager<AppUser>>();
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await context.Database.MigrateAsync();
await Seed.SeedData(context, userManager, roleManager);
} catch(Exception ex) {
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occured during migration");
}
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
await app.RunAsync();
}`
What am I missing here?
Add the roles to the claims, something like this:
public string CreateToken(AppUser user, string[] roles)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Email, user.Email)
};
foreach (var userRole in roles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole));
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["TokenKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
Also, if I remember correctly, NameIdentifier claim has to be first in order to have Identity work with signalr, in case you are planning to use it.
I want to implement OAuth authorization on web api. I am generating JWT token using microsoft graph api
public Token GetToken()
{
try
{
string baseAddress = "https://login.windows.net/<tenantid>/oauth2/token?api-version=1.0";
var client = new HttpClient();
var form = new Dictionary<string, string>{
{
"grant_type",
"client_credentials"
},
{
"client_id",
"<clientid>"
},
{
"client_secret",
"<clientsecret>"
}
};
var tokenResponse = client.PostAsync(baseAddress, new FormUrlEncodedContent(form)).Result;
var token = tokenResponse.Content.ReadAsAsync<Token>(new[] { new JsonMediaTypeFormatter() }).Result;
return token;
}
catch (Exception exc)
{
return new Token();
}
}
I am getting back jwt token but my controller is not able to authorize this. My Startup.cs looks like this:
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
var OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
TokenEndpointPath = new PathString("/oauth2/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60), //token expiration time
Provider = new OAuthProvider(),
};
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
app.UseOAuthAuthorizationServer(oAuthServerOptions);
}
I am running dotnet Web API with basic OAuth token authentication, but i have to configure this API to authenticate from Keycloak server.
I have to configure my asp.net mvc web api (.net framework 4.7) or dotnet core 3 api with keycloak. i have read the keycloak docs but there is no certain examples or adapters so that i can configure my REST API.
This is my startup.cs
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
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.FromHours(6),
AllowInsecureHttp = false
};
app.UseOAuthBearerTokens(OAuthOptions);
}
}
This is the OAuthProvider Class
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException(nameof(publicClientId));
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
string usernameVal = context.UserName;
string passwordVal = context.Password;
long convertusernametoid = long.Parse(usernameVal);
var user = "userfromdb";
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var claims = new List<Claim>();
var username = "username";
claims.Add(new Claim(ClaimTypes.Name, username));
ClaimsIdentity oAuthClaimIdentity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesClaimIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(username);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthClaimIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesClaimIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string> { };
return new AuthenticationProperties(data);
}
}
Please help me with keycloak configuration to my Web Api.
Is it possible to update this code to get token from keycloak, if yes please guide me with configuration or any helpful resource.
Thank You.
I've read through numerous samples, as well as the IdentityServer 4 documentation, but I still seem to be missing something.
Basically, I have IdentityServer4 working to the point that it is proving me an AccessToken and a RefreshToken. I then try to use that AccessToken and sent an HTTP request to my WebAPI2 (.NET 4.5, OWIN), which uses IdentityServer3.AccessTokenValidation which should be compatible based on samples/tests at https://github.com/IdentityServer/CrossVersionIntegrationTests/
The WebAPI2 is giving me at HTTP 400 when I try to access a resource which required Authorization, and I am truly clueless as to why it happens.
Here is the code:
QuickstartIdentityServer Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var connectionString = #"server=(localdb)\mssqllocaldb;database=IdentityServer4.Quickstart.EntityFramework;trusted_connection=yes";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// configure identity server with in-memory stores, keys, clients and scopes
var identityServerConfig = services.AddIdentityServer()
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddSigningCredential(new X509Certificate2(Path.Combine(_environment.ContentRootPath, "certs", "IdentityServer4Auth.pfx"), "test"));
identityServerConfig.Services.AddTransient<IResourceOwnerPasswordValidator, ActiveDirectoryPasswordValidator>();
identityServerConfig.Services.AddTransient<IProfileService, CustomProfileService>();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
InitializeDatabase(app);
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
QuickstartIdentityServer Config.cs (that I used to seed my database)
public class Config
{
// scopes define the API resources in your system
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
{
Scopes = new [] { new Scope("api1"), new Scope("offline_access") },
UserClaims = { ClaimTypes.Role, "user" }
}
};
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" }
},
// resource owner password grant client
new Client
{
ClientId = "ro.client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
UpdateAccessTokenClaimsOnRefresh = true,
AllowedScopes = { "api1", "offline_access" },
AbsoluteRefreshTokenLifetime = 86400,
AllowOfflineAccess = true,
RefreshTokenUsage = TokenUsage.ReUse
}
};
}
}
WebAPI2 Startup.cs
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "http://localhost:44340/",
RequiredScopes = new[] { "api1" },
DelayLoadMetadata = true
});
WebApiConfig.Register(config);
app.UseWebApi(config);
}
WebAPI2 TestController
public class TestController : ApiController
{
// GET: api/Test
[Authorize]
public async Task<IHttpActionResult> Get()
{
return Json(new { Value1 = "value1", Value2 = "value2" });
}
}
ConsoleApplication to test this:
private static async Task MainAsync()
{
// discover endpoints from metadata
//DiscoveryClient client = new DiscoveryClient("https://dev-ea-authapi");
DiscoveryClient client = new DiscoveryClient("http://localhost:44340/");
client.Policy.RequireHttps = false;
var disco = await client.GetAsync();
// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("likosto", "CrM75fnza%");
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
//var newTokenResponse = await tokenClient.RequestRefreshTokenAsync(tokenResponse.RefreshToken);
// call api
var httpClient = new HttpClient();
httpClient.SetBearerToken(tokenResponse.AccessToken);
var response = await httpClient.GetAsync("http://localhost:21715/api/test");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
// HTTP StatusCode = 400 HERE <======================
}
else
{
var content = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(JArray.Parse(content));
}
}
After looking at this more closely, it was because I was adding some very large data sets to my token as experimentation. IIS was sending the HTTP 400 because the request headers were too long.
For my thesis Project i have to implement a token-based (Bearer) Authentication in my ASP.NET solution. I implemented it like Taiseer Jouseh (http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity).
The basic part is working correctly. I have a Mobile Client on which i can register a new User. Then i can Login and receive the token. When i then make a request, the token is sent in the Request Header. It all works fine.
My problem is, that I get an 401 Unauthorized error if I call a [Authorize] Method, even if i send the token. So i removed the [Authorize] Annotation to test some things:
var z = User.Identity;
var t = Thread.CurrentPrincipal.Identity;
var y = HttpContext.Current.User.Identity;
var x = Request.GetOwinContext().Authentication.User.Identity;
Here i got alwas the same Identity: AuthenticationType=null; IsAuthenticated=false; Name=null; Claims:empty
var token = Request.Headers.Authorization;
Here i get the right token. So the token is sent by the request.
I hope you can help me. I have the token but no identity.
Here are parts of my code:
OAuthServiceProvider:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public async override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
// POST /token
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var userManager = DependencyResolver.Current.GetService<UserManager<IdentityUser, int>>();
IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = await userManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
The Controller Method:
#region GET /user/:id
[HttpGet]
[Route("{id:int:min(1)}")]
[ResponseType(typeof(UserEditDto))]
public async Task<IHttpActionResult> GetUser(int id)
{
try
{
// tests
var z = User.Identity;
var t = Thread.CurrentPrincipal.Identity;
var y = HttpContext.Current.User.Identity;
var x = Request.GetOwinContext().Authentication.User.Identity;
var token = Request.Headers.Authorization;
User user = await _userManager.FindByIdAsync(id);
if (user == null)
{
return NotFound();
}
Mapper.CreateMap<User, UserEditDto>();
return Ok(Mapper.Map<UserEditDto>(user));
}
catch (Exception exception)
{
throw;
}
}
#endregion
The WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
config.MapHttpAttributeRoutes();
var corsAttr = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(corsAttr);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Startup:
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
config.DependencyResolver = new UnityDependencyResolver(container);
//config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
WebApiConfig.Register(config);
app.UseWebApi(config);
ConfigureOAuth(app);
}
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.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
}
Finally I found the problem. It is so simple, that I can't believe I spent more than a week to solve this problem.
The problem was in the startup. I simply had to call ConfigureOAuth(app); before app.UseWebApi(config);
So the correct Startup looks like
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
config.DependencyResolver = new UnityDependencyResolver(container);
//config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
WebApiConfig.Register(config);
ConfigureOAuth(app);
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.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
}
My guess is that the token is not being sent correctly from the client. Are you sending the Authorization token with word "Bearer" prefixed in front ?
the Authorization Header should be something like below -
Authorization:Bearer reNjoIUZBJHCMigQJHzCgMMVUyu4vg
Look in the chrome developer tools network tab to see what is being sent.