OWIN ADFS not returning SecurityToken - asp.net-web-api2

Im trying to generate an ActAs token for another adfs, but the first ADFS i login doesnt return me the SecurityToken.
Am i missing some configuration on OWIN Startup?
Startup.cs
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = ConfigurationManager.AppSettings["ida:Audience"],
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
TokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true,
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
},
Configuration = getWsFederationConfiguration()
}
);
}
private static WsFederationConfiguration getWsFederationConfiguration()
{
WsFederationConfiguration configuration = new WsFederationConfiguration
{
Issuer = ConfigurationManager.AppSettings["wsFederation:trustedIssuer"],
TokenEndpoint = ConfigurationManager.AppSettings["wsFederation:issuer"],
};
configuration.SigningKeys.Add(new X509SecurityKey(new X509Certificate2(Convert.FromBase64String(ConfigurationManager.AppSettings["wsFederation:trustedIssuerSigningKey"]))));
return configuration;
}
How i retrieve the token:
public SecurityToken GetSecuritySAMLToken()
{
ClaimsPrincipal icp = Thread.CurrentPrincipal as ClaimsPrincipal;
ClaimsIdentity claimsIdentity = (ClaimsIdentity)icp.Identity;
BootstrapContext bootstrapContext = claimsIdentity.BootstrapContext as BootstrapContext;
//bootstrapContext is not null and bootstrapContext.Token is not null, however bootstrapContext.SecurityToken is null
return bootstrapContext.SecurityToken;
}
UPDATE 1
Tried converting string(SAMLToken) to SecurityToken, handler.ReadToken returns null.
public SecurityToken GetSecuritySAMLToken()
{
ClaimsPrincipal icp = Thread.CurrentPrincipal as ClaimsPrincipal;
ClaimsIdentity claimsIdentity = (ClaimsIdentity)icp.Identity;
BootstrapContext bootstrapContext = claimsIdentity.BootstrapContext as BootstrapContext;
return GetSecurityTokenFromStringToken(bootstrapContext);
}
private static SecurityToken GetSecurityTokenFromStringToken(BootstrapContext bootstrapContext)
{
var handler = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
SecurityToken ST = handler.ReadToken(bootstrapContext.Token);
return ST;
}

Had to retrive it this way:
private static SecurityToken GetSecurityTokenFromStringToken(BootstrapContext bootstrapContext)
{
Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler handler = new Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler();
handler.Configuration = new Microsoft.IdentityModel.Tokens.SecurityTokenHandlerConfiguration();
XmlReader reader = XmlReader.Create(new StringReader(bootstrapContext.Token));
SecurityToken samlToken = handler.ReadToken(reader);
return samlToken;
}

Related

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?

Role based Authentication: Bearer Token to Cookie using Microsoft .NetCore 3.1

I'm trying to transform.NetCore 3.1 Code from Bearer Token implementation to Cookie-based implementation Also trying to make Role-based authorization work with existing code. Can you please help me to change this code? The below code shows currently how Bearer Token is retrieved and the next part shows how role-based authorization is implemented in code.
Here is the current Bearer Token implementation.
var key = Encoding.ASCII.GetBytes(Configuration["AppSettings:Secret"]);
var signingKey = new SymmetricSecurityKey(key);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
IssuerSigningKey = signingKey,
ValidateIssuer = false,
ValidateAudience = false
};
});
Following annotation currently used for Role-based Authorization -
[Authorize(Roles = "1")]
[Route("api/[controller]")]
[ApiController]
public class JobLogsController : ControllerBase
{
private readonly EtpRepoContext _context;
private IJobLogsRepository _jobLogsRepository;
private IConfiguration _configuration;
public JobLogsController(EtpRepoContext context, IJobLogsRepository jobLogsRepository, IConfiguration configuration)
{
_context = context;
_jobLogsRepository = jobLogsRepository;
_configuration = configuration;
}
// GET: api/JobLogs
[HttpGet]
public async Task<ActionResult<IEnumerable<JobLog>>> GetJobLog()
{
return await _context.JobLog.ToListAsync();
}
// GET: api/JobLogs/5
[HttpGet("{id}")]
[ProducesResponseType(typeof(JobDetail), 200)]
[ProducesResponseType(typeof(string), 400)]
public IActionResult FindById([FromRoute] String id)
{
string contentStr = "";
try
{
if(id.Length >= 10)
{
contentStr = _jobLogsRepository.GetLogById(id);
}
else
{
contentStr = _jobLogsRepository.GetFileById(id);
}
var content = Newtonsoft.Json.JsonConvert.SerializeObject(new { content = contentStr });
return Ok(content);
}
catch (Exception ex)
{
return StatusCode(500, "Internal server error");
}
}
This is how the Microsoft identity model is used to claim the token.
public class ClaimsTransformer : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
ClaimsIdentity claimsIdentity = (ClaimsIdentity)principal.Identity;
// flatten realm_access because Microsoft identity model doesn't support nested claims
// by map it to Microsoft identity model, because automatic JWT bearer token mapping already processed here
if (claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim((claim) => claim.Type == "identity"))
{
var realmAccessClaim = claimsIdentity.FindFirst((claim) => claim.Type == "identity");
dynamic realmAccessAsDict = JsonConvert.DeserializeObject<Object>(realmAccessClaim.Value);
string role = realmAccessAsDict.role.ToString();
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", role));
//var role = realmAccessClaim.
//var realmAccessAsDict = JsonConvert.DeserializeObject<Object>(realmAccessClaim.Value);
/*if (realmAccessAsDict["role"] != null)
{
foreach (var role in realmAccessAsDict["role"])
{
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", role));
}
}*/
}
return Task.FromResult(principal);
}
}
}

Set Authenticated User Globally for Testing in 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();
}

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"));

asp.net core controller not authorize with jwt

I use Microsoft.AspNetCore.Authentication.JwtBearer Library for jwt authentication in ASP.Net Core Web Api.When i request a token, it work fine and i get a token.
But when i send request to secure Action i get 401 status code.
in my Startup Class:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,IOptionsSnapshot<SiteSettings> siteSettings)
{
var jwtOption = siteSettings.Value.JwtOption;
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtOption.Issuer,
ValidateAudience = true,
ValidAudience = jwtOption.Audience,
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = false,
ClockSkew = TimeSpan.Zero
};
app.UseMvc();
app.UseApiCustomIdentityServices();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Audience = jwtOption.Audience,
Authority = jwtOption.Audience,
RequireHttpsMetadata = false,
TokenValidationParameters = tokenValidationParameters
});
}
And my TokenController:
public class TokenController : Controller
{
private readonly IUserManager _userManager;
private readonly IJwtService _jwtService;
private readonly IOptionsSnapshot<SiteSettings> _siteSettings;
private readonly JsonSerializerSettings _serializerSettings;
public TokenController(
IUserManager userManager,
IJwtService jwtService,
IOptionsSnapshot<SiteSettings> siteSettings
)
{
_userManager = userManager;
_jwtService = jwtService;
_siteSettings = siteSettings;
_serializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
}
[HttpPost]
public async Task<IActionResult> Post([FromBody]LoginViewModel model)
{
if (!ModelState.IsValid)
return BadRequest();
var identity = await GetClaimsIdentity(model.Username, model.Password);
if (identity == null)
{
return BadRequest();
}
var response = new
{
id = identity.Claims.Single(c => c.Type == "id").Value,
auth_token = await _jwtService.GenerateEncodedToken(model.Username, identity),
expires_in = (int)_siteSettings.Value.JwtOption.ValidFor.TotalSeconds
};
var json = JsonConvert.SerializeObject(response, _serializerSettings);
return new OkObjectResult(json);
}
private async Task<ClaimsIdentity> GetClaimsIdentity(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
var userToVerify = await _userManager.FindByNameAsync(userName);
if (userToVerify != null)
{
if (await _userManager.CheckPasswordAsync(userToVerify, password))
{
return await Task.FromResult(_jwtService.GenerateClaimsIdentity(userName, userToVerify.Id.ToString()));
}
}
}
return await Task.FromResult<ClaimsIdentity>(null);
}
what's wrong?