Set Authenticated User Globally for Testing in ASP.NET Core - asp.net-core

I am working on an ASP.NET Core 2.2 with ASP.Net Core Identity project.
I would like to set the authenticated User, with its UserId, globally for testing.
It this possible?

For Integration Test, you could progammly login the application, save the cookies and then attach the cookies for sub-requests.
Try to implement custom WebApplicationFactory like
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
});
base.ConfigureWebHost(builder);
}
public new HttpClient CreateClient()
{
var cookieContainer = new CookieContainer();
var uri = new Uri("https://localhost:44344/Identity/Account/Login");
var httpClientHandler = new HttpClientHandler
{
CookieContainer = cookieContainer
};
HttpClient httpClient = new HttpClient(httpClientHandler);
var verificationToken = GetVerificationToken(httpClient, "https://localhost:44344/Identity/Account/Login");
var contentToSend = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Email", "test#outlook.com"),
new KeyValuePair<string, string>("Password", "1qaz#WSX"),
new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken),
});
var response = httpClient.PostAsync("https://localhost:44344/Identity/Account/Login", contentToSend).Result;
var cookies = cookieContainer.GetCookies(new Uri("https://localhost:44344/Identity/Account/Login"));
cookieContainer.Add(cookies);
var client = new HttpClient(httpClientHandler);
return client;
}
private string GetVerificationToken(HttpClient client, string url)
{
HttpResponseMessage response = client.GetAsync(url).Result;
var verificationToken =response.Content.ReadAsStringAsync().Result;
if (verificationToken != null && verificationToken.Length > 0)
{
verificationToken = verificationToken.Substring(verificationToken.IndexOf("__RequestVerificationToken"));
verificationToken = verificationToken.Substring(verificationToken.IndexOf("value=\"") + 7);
verificationToken = verificationToken.Substring(0, verificationToken.IndexOf("\""));
}
return verificationToken;
}
}
And then
public class IntegrationTestWithIdentityTest : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public IntegrationTestWithIdentityTest(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task IndexRendersCorrectTitle()
{
var response = await _client.GetAsync("https://localhost:44344/About");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Send Email", responseString);
}
}
Source Code: IntegrationTestWithIdentityTest.
If you want to mock a user which is not exist in the Identity Table, you need to define a new endpoint which will sign the user with
public async Task<IActionResult> Login()
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "edward"));
identity.AddClaim(new Claim(ClaimTypes.Name, "edward zhou"));
//add your own claims from jwt token
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true });
return View();
}

Related

.asp.net webapi TokenService Value cannot be null

Getting Email From ClaimTypes in asp.net core 6 Give error "Value cannot be null"
When i use AddIdentityCore in the service the methode for geting the current login user from _userManager
Working Fine
var user = await _userManager.FindByEmailAsync(User.FindFirstValue(ClaimTypes.Email));
services.AddIdentityCore<AppUser>(opt =>
{
opt.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<DataContext>()
.AddSignInManager<SignInManager<AppUser>>();
But if i change the AddIdentityCore to AddIdentity as below then cant get the user from _userManager
services.AddIdentity<AppUser, IdentityRole>(opt =>
{
opt.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<DataContext>()
.AddSignInManager<SignInManager<AppUser>>();
And Give the Follwoing Error
"statusCode": 500,
"message": "Value cannot be null. (Parameter \u0027email\u0027)",
"details": " at Microsoft.AspNetCore.Identity.UserManager\u00601.FindByEmailAsync(String email)
I want to use IdentityRole in service to allow me save Asp.net User Rolls and assign them to user
Here is my Token Service which create JWT Token
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
namespace API.Services
{
public class TokenService
{
private readonly IConfiguration _config;
public TokenService(IConfiguration config)
{
_config = config;
}
public string CreateToken(AppUser user)
{
var claim = 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["MyTokenKey"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claim),
Expires = DateTime.Now.AddDays(7),
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}
}

ASP.NET Core application http client request to a web api

I have a running Web API and I try to get a bearer token back from it. Starting the request from Postman is working and I get the token back. Once doing from my application I always get an http 400 Bad Request error.
What am I missing here?
public async Task<string> GetToken(string userName, string passWord)
{
var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
request.Headers.Host = "api.my-host.com";
request.Headers.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);
return authResult == null ? "" : authResult.Access_Token;
}
As requested here's a screenshot of the Postman result:
I created a HttpGet request and added the bearer token from Postman in the code and I receive data. Just the token request seems to have a problem.
And my controller:
namespace AmsAPI.Controller
{
[Produces("application/json")]
[Route("api/auth")]
[ApiController]
[Authorize]
public class AuthenticationController : ControllerBase
{
private readonly IAuthenticationManager _authenticationManager;
public AuthenticationController(IAuthenticationManager authenticationManager)
{
_authenticationManager = authenticationManager;
}
[HttpPost("login"), AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<ActionResult> Login([FromHeader] byte[] basic)
{
if (!ModelState.IsValid) return BadRequest();
string Basic = Encoding.UTF8.GetString(basic);
var splitBasic = Basic.Split(':');
AuthCredentials credentials = new()
{
UserName = splitBasic[0],
Password = splitBasic[1]
};
return await _authenticationManager.SignInCheck(credentials) ?
Ok(new
{
message = string.Format("User {0} successfully logged in.", credentials.UserName),
access_token = await _authenticationManager.CreateToken(),
token_type = "bearer",
expires_in = "3600"
}) :
Unauthorized();
}
[HttpGet("user")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<List<User>> GetUser() => await _authenticationManager.GetUser();
}
}
Well I have successfully reproduce your issue.
You are getting 400 because its searching for SSL credentials but
by default we don't have certificate bind with our request and its not
required. So to handle this 400 exception you have to use
HttpClientHandler which will bypass certificate error. So you can
try below way to get the token response successfully.
HTTP Request:
public async Task<string> GetToken()
{
var user = new UserCred();
user.user_name = "admin";
user.password = "123456";
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
var client = new HttpClient(handler);
var json = JsonConvert.SerializeObject(user);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var url = "http://localhost:21331/api/Authentication/login";
var response = await client.PostAsync(url, data);
string result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
return result;
}
Output:
Note: You need to add below code snippet to resolve your issue. But I like to call HTTP POST request in above ways to make it little
clean. You can continue with your code just adjust what I suggest.
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
var client = new HttpClient(handler);
Hope above steps resolve your problem accordingly.
It appeared that Postman is sending the Basic Authorization Header as Content of the Http request and not in the Header, but my Web App was implementing the Authorization correctly in the Header.
So in the WebApi it looks like
[Authorize]
[Route("api/auth")]
[ApiController]
public class AuthenticationController : ControllerBase
{
private readonly IAuthenticationManager _authenticationManager;
public AuthenticationController(IAuthenticationManager authenticationManager)
{
_authenticationManager = authenticationManager;
}
[HttpPost("login"), AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<ActionResult> Login()
{
//check if header has Authorization
if (!Request.Headers.ContainsKey("Authorization")) return BadRequest();
try
{
AuthenticationHeaderValue authenticationHeaderValue = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
if (authenticationHeaderValue == null) throw new Exception();
var bytes = Convert.FromBase64String(authenticationHeaderValue.Parameter ?? throw new Exception());
string[] creds = Encoding.UTF8.GetString(bytes).Split(':');
AuthCredentials credentials = new()
{
UserName = creds[0],
Password = creds[1]
};
return await _authenticationManager.SignInCheck(credentials) ?
Ok(new
{
message = string.Format("User {0} successfully logged in.", credentials.UserName),
access_token = await _authenticationManager.CreateToken(),
token_type = "bearer",
expires_in = "3600"
}) :
Unauthorized();
}
catch (Exception)
{
return BadRequest();
}
}
}
and my request like following:
public async Task<string> GetToken(string userName, string passWord)
{
//set request
var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");
//set Header
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
//get response
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);
var token = authResult.Access_Token;
return authResult == null ? "Authorization failed!" : "Bearer token successfully created!";
}
and the httpClient is outsourced into a Service
public static void ConfigureServices(this IServiceCollection services)
{
var url = Environment.GetEnvironmentVariable("amsApiUrl");
var host = Environment.GetEnvironmentVariable("amsHostUrl");
//set HttpClient
services.AddHttpClient<IAmsAccountService, AmsAccountService>(c =>
{
c.BaseAddress = new Uri(url ?? "");
c.DefaultRequestHeaders.Accept.Clear();
c.DefaultRequestHeaders.Host = host;
c.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
c.Timeout = TimeSpan.FromSeconds(10);
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
CookieContainer = new CookieContainer(),
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
{
return true;
}
};
});
}

How do i avoid ObjectDisposedException when i access the db with EF Core via API Requests for JWT Auth?

I am working on a API right now where i can send /api/login get a jwt token and use it further to do some stuff on that api with my client app later
thats my startup where i add the services :
services.AddScoped<IUserCoreController>(x => new UserCoreController(x.GetRequiredServic<TerraContext>(), x.GetRequiredService<IHashEngine>()));
services.AddScoped<ITokenFactory>(x => new TokenFactory(x.GetRequiredService<IConfiguration>(), x.GetService<JwtSettings>(), x.GetService<AppDataSettings>(),x.GetRequiredService<IUserCoreController>()));
With this method i try to access to my db _context and check if a user with that email exists and return true/false
public async Task<ResponseModel> Authenticate(string email, string password)
{
using (var db = _context)
{
//logic of my method
}
the code below shows the constructor for this class and how i inject the dbcontext and a hashengine
public class UserCoreController : IUserCoreController
{
TerraContext _context;
IHashEngine _hashEngine;
public UserCoreController(TerraContext context, IHashEngine engine)
{
_hashEngine = engine;
_context = context;
}
until now i get a true from this method after that i generate a jwt token with the class below
{
public class TokenFactory : ITokenFactory
{
IUserCoreController _userCore;
TerraContext _context;
IConfiguration _config;
JwtSettings _jwtSettings;
AppDataSettings _appDataSettings;
public TokenFactory(IConfiguration config, JwtSettings jwtsettings, AppDataSettings appDataSettings, IUserCoreController userCore)
{
_config = config;
_jwtSettings = jwtsettings;
_appDataSettings = appDataSettings;
_userCore = userCore;
}
public async Task<ResponseModel> Authenticate(LoginModel model)
{
var response = await _userCore.Authenticate(model.Email, model.Password);
return response;
}
public async Task<TokenResponse> GenerateJwtToken(string id)
{
var token = new TokenResponse();
var user = await _userCore.GetUser(id);
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new List<Claim>();
if (!String.IsNullOrEmpty(user.Firstname))
claims.Add(new Claim("firstname", user.Firstname));
if (!String.IsNullOrEmpty(user.Lastname))
claims.Add(new Claim("lastname", user.Lastname));
if (!String.IsNullOrEmpty(user.Username))
claims.Add(new Claim("userId", user.UserId));
if (!String.IsNullOrEmpty(user.Username))
claims.Add(new Claim("appId", _appDataSettings.ApplicationId));
if (!String.IsNullOrEmpty(user.Username))
claims.Add(new Claim("orgId", _appDataSettings.OrganisationId));
var jwt = new JwtSecurityToken(
issuer: _jwtSettings.Issuer,
audience: _jwtSettings.Audience,
expires: DateTime.Now.AddHours(3),
signingCredentials: credentials,
claims: claims
);
var tokstring = new JwtSecurityTokenHandler().WriteToken(jwt);
token.Token = tokstring;
token.Email = user.Email;
token.TokenId = Guid.NewGuid().ToString();
return token;
}
public async Task<bool> ValidateToken(string token)
{
await Task.Delay(100);
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
SecurityToken validatedToken;
IPrincipal principal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
return true;
}
public async Task<ClaimsPrincipal> GetUser(string token)
{
await Task.Delay(100);
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetValidationParameters();
SecurityToken validatedToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
return principal;
}
private TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidAudience = _jwtSettings.Audience,
ValidIssuer = _jwtSettings.Issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey)),
ClockSkew = TimeSpan.Zero
};
}
}
I get the Exception System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Should i just stop coding with a using() statement with my DbContext?

Authenticate Google JWT

I am attempting to authenticate a google jwt bearer token from my .net core webapi application and continually receive 401s. I have verified that the token is valid via jwt.io. I am trying to implement the solution offered here,
google-jwt-authentication-with-aspnet-core-2-0
Can anyone see what is wrong with my code?
Below is my code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Configure SnapshotCollector from application settings
services.Configure<SnapshotCollectorConfiguration>(Configuration.GetSection(nameof(SnapshotCollectorConfiguration)));
// Add SnapshotCollector telemetry processor.
services.AddSingleton<ITelemetryProcessorFactory>(sp => new SnapshotCollectorTelemetryProcessorFactory(sp));
conString = Microsoft
.Extensions
.Configuration
.ConfigurationExtensions
.GetConnectionString(this.Configuration, "DefaultConnection");
services.AddDbContext<GotNextDBContext>(
options =>
options.UseSqlServer(conString));
services.AddTransient<ILocationService, LocationService>();
services.AddTransient<ICompanyService, CompanyService>();
services.AddTransient<IUserLocationLogService, UserLocationLogService>();
services.AddTransient<IUserService, UserService>();
services.AddTransient<ILanguageService, LanguageService>();
services.AddTransient<IGenderService, GenderService>();
services.AddTransient<ISportService, SportService>();
services.AddTransient<IMeasurementService, MeasurementService>();
var clientIds = new List<string>();
clientIds.Add("[myClientId]");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.SecurityTokenValidators.Clear();
o.SecurityTokenValidators.Add(new GoogleTokenValidator(clientIds: clientIds ));
});
services.AddRouting();
services.AddAutoMapper();
services.AddAntiforgery(options =>
{
options.Cookie.Name = "X-CSRF-TOKEN-GOTNEXT-COOKIE";
options.HeaderName = "X-CSRF-TOKEN-GOTNEXT-HEADER";
options.SuppressXFrameOptionsHeader = false;
});
var serviceProvider = services.BuildServiceProvider();
var context = serviceProvider.GetService<GotNextDBContext>();
}
GoogleTokenValidator.cs
public class GoogleTokenValidator : ISecurityTokenValidator
{
private readonly JwtSecurityTokenHandler _tokenHandler;
private readonly IEnumerable<string> _clientIds;
public GoogleTokenValidator()
{
_tokenHandler = new JwtSecurityTokenHandler();
}
public GoogleTokenValidator(IEnumerable<string> clientIds)
{
_tokenHandler = new JwtSecurityTokenHandler();
_clientIds = clientIds;
}
public bool CanValidateToken => true;
public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;
public bool CanReadToken(string securityToken)
{
return _tokenHandler.CanReadToken(securityToken);
}
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
validatedToken = null;
var payload = GoogleJsonWebSignature.ValidateAsync(securityToken, new GoogleJsonWebSignature.ValidationSettings() { Audience = _clientIds }).Result; // here is where I delegate to Google to validate
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, payload.Name),
new Claim(ClaimTypes.Name, payload.Name),
new Claim(JwtRegisteredClaimNames.FamilyName, payload.FamilyName),
new Claim(JwtRegisteredClaimNames.GivenName, payload.GivenName),
new Claim(JwtRegisteredClaimNames.Email, payload.Email),
new Claim(JwtRegisteredClaimNames.Sub, payload.Subject),
new Claim(JwtRegisteredClaimNames.Iss, payload.Issuer),
};
try
{
var principle = new ClaimsPrincipal();
principle.AddIdentity(new ClaimsIdentity(claims));
return principle;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
I am hitting the endpoint from the belowHttpClient call in Xamarin Forms.
using (var client = new HttpClient() { BaseAddress = new Uri("https://gotnext.azurewebsites.net/api/user/post/") })
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add(verificationToken.tokenName, verificationToken.token);
//client.DefaultRequestHeaders.Add("X-ZUMO-AUTH", googleUser.GoogleAuthToken);
client.DefaultRequestHeaders.Add("Bearer", googleUser.GoogleIdToken);
var json = JsonConvert.SerializeObject(newUser);
var content = new StringContent(json, Encoding.UTF8, "application/json");
response = client.PostAsync(client.BaseAddress, content).Result;
}
The ClaimsIdentity that you add to your ClaimsPrincipal does not have an AuthenticationType set so IsAuthenticated will always be false (source).
return new ClaimsPrincipal(new ClaimsIdentity(claims, "Google"));
You can set the authentication type to any value as long as it's not empty.
Stepping throw the code locally, this line: return new ClaimsPrincipal(new
ClaimsIdentity(claims, "Google")); Is returning an authenticated identity. However it
still is returning a 401. I feel like I'm so close!!
I am making the authentification with google in my web application too(and i walked from there: enter link description here) and i spended much time to investigate these all.
In your solution just add these pieces:
1)
//at "ConfigureServices" in "Startup.cs":
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
2) How was said above that it is requied too
return new ClaimsPrincipal(new ClaimsIdentity(claims, "Google"));

Why [Authorize] attribute return 401 status code JWT + Asp.net Web Api?

I'm having big trouble finding issue with the JWT token authentication with asp.net web api. This is first time I am dealing with JWT & Web Api authentication & Authorization.
I have implemented the following code.
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new OAuthTokenProvider(),
RefreshTokenProvider = new RefreshTokenProvider(),
AccessTokenFormat = new Provider.JwtFormat("http://localhost:49860")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = "http://localhost:49860";
string audienceId = Config.AudienceId;
byte[] audienceSecret = TextEncodings.Base64Url.Decode(Config.AudienceSecret);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
OAuthTokenProvider.cs
public class OAuthTokenProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// validate client credentials (demo)
// should be stored securely (salted, hashed, iterated)
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
/***Note: Add User validation business logic here**/
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{ "as:client_id", "Kaushik Thanki" }
});
ClaimsIdentity oAuthIdentity = new ClaimsIdentity("JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, props);
context.Validated(ticket);
}
}
JwtFormat.cs
public class JwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer = string.Empty;
public JwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = Config.AudienceId;
string symmetricKeyAsBase64 = Config.AudienceSecret;
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}
RefreshTokenProvider.cs
public class RefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
// maybe only create a handle the first time, then re-use for same client
// copy properties and set the desired lifetime of refresh token
var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddYears(1)
};
var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);
//_refreshTokens.TryAdd(guid, context.Ticket);
_refreshTokens.TryAdd(guid, refreshTokenTicket);
// consider storing only the hash of the handle
context.SetToken(guid);
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(context.Token, out ticket))
{
context.SetTicket(ticket);
}
}
}
Now Once I pass the authentication (Which I kept dummy for initial level matching same username & password) & got the token & refresh token.
When I request for method that is decorated with [Authorize] attribute, I always gets 401 status code.
I testing this method in postman following way
Any help or guidance will be really appreciated. I have invested my two days finding the solution for this but all in vain.