Login from Universal App to Web Api using Live Id - asp.net

I'm trying to implement following functionality:
User signs in into Live Id account from Windows Phone 8.1 (or Universal) app.
App accesses Web Api that I develop with ASP.NET Web Api 2
In this Web Api I need to authenticate the user.
Later, I want to authenticate same user in web app
Here is what I'm doing, and it doesn't work.
In my Windows Phone App:
var authClient = new LiveAuthClient("http://myservice.cloudapp.net");
LiveLoginResult result = await authClient.LoginAsync(new string[] { "wl.signin" });
if (result.Status == LiveConnectSessionStatus.Connected)
{
connected = true;
var identity = await ConnectToApi(result.Session.AuthenticationToken);
Debug.WriteLine(identity);
}
And then
private async Task<string> ConnectToApi(string token)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://myservice.cloudapp.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// HTTP GET
HttpResponseMessage response = await client.GetAsync("api/values");
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return result;
}
else
return response.ReasonPhrase;
}
}
And then in my web api I have following
public void ConfigureAuth(IAppBuilder app)
{
app.UseMicrosoftAccountAuthentication(
clientId: "my client id",
clientSecret: "my secret");
}
I registered http://myservice.cloudapp.net as redirect url.
The problem is authentication doesn't work, web api actions do not recognize the user.

I got it totally wrong. First, I actually need to use app.UseJwtBearerAuthentication method. The example was found here http://code.lawrab.com/2014/01/securing-webapi-with-live-id.html. But when I tried, I got this error in the output
IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
)
This one took me a while to figure out, until I found this post: JwtSecurityTokenHandler 4.0.0 Breaking Changes?
Putting these things together, I got the solution that seems to work now in my testing environment:
public void ConfigureAuth(IAppBuilder app)
{
var sha256 = new SHA256Managed();
var sKey = "<Secret key>" + "JWTSig";
var secretBytes = new UTF8Encoding(true, true).GetBytes(sKey);
var signingKey = sha256.ComputeHash(secretBytes);
var securityKeyProvider = new SymmetricKeyIssuerSecurityTokenProvider("urn:windows:liveid", signingKey);
var securityKey = securityKeyProvider.SecurityTokens.First().SecurityKeys.First();
var jwtOptions = new JwtBearerAuthenticationOptions()
{
//AllowedAudiences = new[] { "<url>" },
//IssuerSecurityTokenProviders = new[]
//{
// new SymmetricKeyIssuerSecurityTokenProvider("urn:windows:liveid",signingKey)
//},
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
{
IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, validationParameters) =>
{
return securityKey;
},
ValidAudience = "<url>",
ValidIssuer = securityKeyProvider.Issuer
}
};
app.UseJwtBearerAuthentication(jwtOptions);
}

For anybody looking to do this from JavaScript I managed to get this working by following steps from this blog. You can find the audience by putting your token through jwt.io
https://blog.dirk-eisenberg.de/2014/08/30/validate-authentication_token-from-microsoft-liveid-with-node-express-jwt/
const validateLiveJWT = (token) => {
const secret = '<<SECRET>>';
const sha256 = crypto.createHash('sha256');
sha256.update(secret + 'JWTSig', 'utf8');
const secretBase64 = sha256.digest('base64');
const secret = new Buffer(secretBase64, 'base64');
const options = {
audience: '<<AUDIENCE>>',
issuer: 'urn:windows:liveid',
};
return new Promise((resolve) => {
jwt.verify(token, secret, options, (err: any, claims: any) => {
if (err) {
resolve(undefined);
} else {
resolve(claims);
}
});
});
}

Related

ASP.NET MVC OpenIdConnect OnAuthorizationCodeReceived not triggering after app restart

I work in a corporate and we have got an AAD for all the users in our organisation.
I am working on creating an app that authenticates users with AAD using OpenIdConnect. Now, I have followed this sample from Azure AD B2C. it works fine when I run it locally for the first time (after an hour break) and OnAuthorizationCodeReceived is triggered, however, when I stop and restart the app in visual studio OnAuthorizationCodeReceived doesn't get triggered and as a result, I get a null User (IAccount) when trying to retrieve token silently in this code block
public async Task<string> GetAccessToken(string scopes)
{
//var userClaims = User.Identity as System.Security.Claims.ClaimsIdentity;
var userClaims2 = ClaimsPrincipal.Current.Claims;
IConfidentialClientApplication cc = MsalAppBuilder.BuildConfidentialClientApplication();
var userAccount = await cc.GetAccountAsync(ClaimsPrincipal.Current.GetMsalAccountId());
var userAccount2 = await cc.GetAccountsAsync(ClaimsPrincipal.Current.FindFirst(Globals.ObjectIdClaimType).Value);
var userAccount3 = userAccount2.FirstOrDefault();
AuthenticationResult result = await cc.AcquireTokenSilent(new string[] { scopes }, userAccount3).ExecuteAsync();
return result.AccessToken;
}
My startup.Auth file looks like this:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
// Configure OpenIDConnect, register callbacks for OpenIDConnect Notifications
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = ConfigHelper.ClientId,
Authority = String.Format(CultureInfo.InvariantCulture, aadInstance, ConfigHelper.Tenant),
PostLogoutRedirectUri = ConfigHelper.PostLogoutRedirectUri,
RedirectUri = ConfigHelper.PostLogoutRedirectUri,
ResponseType = OpenIdConnectResponseTypes.CodeIdToken,
// ValidateIssuer set to false to allow work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name or Id (example: contoso.onmicrosoft.com)
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
//string redirectURI = string.Format("{0}://{1}{2}/", context.Request.Scheme, context.Request.Host, context.Request.PathBase);
string redirectURI = string.Format("https://{0}{1}/", context.Request.Host, context.Request.PathBase);
context.ProtocolMessage.RedirectUri = redirectURI;
context.ProtocolMessage.DomainHint = domain_hint;
return Task.FromResult(0);
},
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = (context) =>
{
if (ConfigHelper.NonceExceptionHandler && (context.Exception.Message.StartsWith("OICE_20004") || context.Exception.Message.Contains("IDX10311") || context.Exception.Message.Contains("IDX21323")))
{
context.SkipToNextMiddleware();
return Task.FromResult(0);
}
return Task.FromResult(0);
}
}
});
app.Use<MsOfficeLinkPrefetchMiddleware>();
// add this function into the app pipeline to Call my function OnAuth and call the next in the pipeline
app.Use((context, next) =>
{
// The function to call...
OnAuth(context);
return next.Invoke();
});
// limit the calls to the above function to be in the PostAuthenticate part of the stage
app.UseStageMarker(PipelineStage.PostAuthenticate);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
string redirectURI = string.Format("{0}://{1}{2}/", notification.Request.Scheme, notification.Request.Host, notification.Request.PathBase);
IConfidentialClientApplication confidentialClient = MsalAppBuilder.BuildConfidentialClientApplication(new ClaimsPrincipal(notification.AuthenticationTicket.Identity));
// Upon successful sign in, get & cache a token using MSAL
//user.readbasic.all
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(new[] { "user.readbasic.all" }, notification.Code).ExecuteAsync();
}
I have been trying for a few days to solve this problem but to no avail. Please Help.
Cheers

.Net Core Refresh Token Not Working after Publish

I have an web application written in .Net Core both Backend + Frontend
I have login page with Email + Password + Remember Me ( Checkbox )
I want if user click on remember me stay signed in forever until he/she clear cookies.
When user logging in I save ACCESS_TOKEN + REFRESH_TOKEN in Principals Here is example of Login Method:
public async Task<IActionResult> LoginAsync(LoginViewModel loginViewModel)
{
if (ModelState.IsValid)
{
var requestDto = _mapper.Map<LoginRequestDto>(loginViewModel);
try
{
var response = await _authenticationClient.LoginAsync(requestDto);
var claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(CustomUserClaimTypes.AccessToken, response.AccessToken),
new Claim(CustomUserClaimTypes.RefreshToken, response.RefreshToken),
new Claim(CustomUserClaimTypes.FullName, response.Fullname),
new Claim(CustomUserClaimTypes.UserName, response.Username),
new Claim(CustomUserClaimTypes.UserId, response.UserId),
new Claim(CustomUserClaimTypes.Email, response.Email),
new Claim(CustomUserClaimTypes.TokenExpireTime, response.TokenExpire.ToString())
}, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(scheme: CookieAuthenticationDefaults.AuthenticationScheme);
var principals = new ClaimsPrincipal(claimsIdentity);
if (loginViewModel.IsPersistent)
{
await HttpContext.SignInAsync(scheme: CookieAuthenticationDefaults.AuthenticationScheme, principals, new AuthenticationProperties
{
IsPersistent = true,
AllowRefresh = true,
IssuedUtc = DateTime.Now
});
}
else
{
await HttpContext.SignInAsync(scheme: CookieAuthenticationDefaults.AuthenticationScheme, principals, new AuthenticationProperties { IsPersistent = false, AllowRefresh = true });
}
return new JsonResult(new
{
success = true,
result = "/home"
});
}
catch (ApiException ex)
{
if (ex.ErrorResponse != null)
{
return new JsonResult(new
{
success = false,
result = ex.ErrorResponse.Errors.Select(x => x.Message)
});
}
if (ex.Response != null)
{
return new JsonResult(new
{
success = false,
result = ex.Response
});
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
return View("Index");
}
Token Expire Time is 5 Minutes, I want if user clicked remember me after this 5 minutes I need to refresh token to make sure access token is valid
Here is example of Refresh Token :
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Authentication/Index";
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = async x =>
{
var identity = (ClaimsIdentity)x.Principal.Identity;
var tokenExpire = identity.FindFirst(CustomUserClaimTypes.TokenExpireTime);
if (DateTime.Parse(tokenExpire.Value) < DateTime.Now)
{
var accessTokenClaim = identity.FindFirst(CustomUserClaimTypes.AccessToken);
var refreshTokenClaim = identity.FindFirst(CustomUserClaimTypes.RefreshToken);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://test.api.com/api/v1/");
var requestData = new RefreshTokenRequestApiDto
{
AccessToken = accessTokenClaim.Value,
RefreshToken = refreshTokenClaim.Value
};
var jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(requestData);
var stringContent = new StringContent(jsonData, Encoding.UTF8, "application/json");
var result = await client.PostAsync("auth/refresh-token", stringContent);
if (result.IsSuccessStatusCode)
{
var deserializedData = await result.Content.ReadAsStringAsync();
var mapped = Newtonsoft.Json.JsonConvert.DeserializeObject<BaseApiResponse<BaseAuthenticationResponseDto>>(deserializedData);
if (mapped != null)
{
identity.RemoveClaim(accessTokenClaim);
identity.RemoveClaim(refreshTokenClaim);
identity.RemoveClaim(tokenExpire);
identity.AddClaims(new[]
{
new Claim(CustomUserClaimTypes.AccessToken, mapped.Result.AccessToken),
new Claim(CustomUserClaimTypes.RefreshToken, mapped.Result.RefreshToken),
new Claim(CustomUserClaimTypes.TokenExpireTime, mapped.Result.TokenExpire.ToString())
});
x.ShouldRenew = true;
}
}
}
}
}
};
});
The problem is when I try on localhost everything working good, User is persist, even I turn off my PC, But when I publish this to my PLESK panel, and Try it from there, after 5 minutes user signed out and on cookie the AspNet.Cookies is exist but user seems unauthorized, I need to login again after 5 minutes continuously.
Anything wrong on my code ?
Or I need to do something on my Plesk Panel ?
SORRY ABOUT MY ENGLISH :)
Thanks!

How to use subdomain to delegate client id for authentication into Azure B2C

I'm trying to stand up a dot net core 2.2 web app that will use the subdomain to determine what client id I should use to authenticate the user in Azure B2C. I have an API that I'm calling that will provide the client id for the subdomain.
Is it possible to swap out client ids at run time or does that have to be configured at start-up?
Startup.cs
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => Configuration.Bind("Authentication:AzureAdB2C", options))
.AddCookie();
OnRedirectToIdentityProvider Event
public async Task OnRedirectToIdentityProvider(RedirectContext context)
{
var defaultClientId = AzureAdB2COptions.ClientId;
var fullAddress = context.HttpContext?.Request?.Headers?["Host"].ToString()?.Split(':');
var subdomain = fullAddress[0];
var tenant = await _api.GetConfig(subdomain);
if (tenant != null &&
!tenant.ClientId.Equals(defaultClientId))
{
context.Options.ClientId = tenant.ClientId;
}
var defaultPolicy = AzureAdB2COptions.DefaultPolicy;
if (context.Properties.Items.TryGetValue(AzureAdB2COptions.PolicyAuthenticationProperty,
out var policy) &&
!policy.Equals(defaultPolicy))
{
context.ProtocolMessage.Scope = AzureAdB2COptions.ApiScopes;
context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
context.ProtocolMessage.IssuerAddress = context.ProtocolMessage.IssuerAddress
.ToLower().Replace(defaultPolicy.ToLower(), policy.ToLower());
context.Properties.Items.Remove(AzureAdB2COptions.PolicyAuthenticationProperty);
}
else
{
context.ProtocolMessage.Scope = AzureAdB2COptions.ApiScopes;
}
return;
}
When the app loads, the initial configuration works: client1.domain.com is mapped to the client id configured at start up and logins work correctly. If I change the subdomain I swap out the client id in the OnRedirectToIdentityProvider event, but am returned a IDX10214: Audience validation failed error.
Well that sucked, but I had to specify valid audiences on the token validation params in my open-id config.
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = AzureAdB2COptions.ClientId;
options.Authority = AzureAdB2COptions.Authority;
options.UseTokenLifetime = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = "name",
ValidAudiences = new[] { "clientid1", "clientid2", "etca" }
};
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
OnRemoteFailure = OnRemoteFailure,
OnAuthorizationCodeReceived = OnAuthorizationCodeReceived
};
}

How to use refresh token to get a new access token from identity server 4 with Xamarin.Forms client

How to use refresh_token to obtain a new access token from identity server in Xamarin.Forms client?
I followed tutorial https://sinclairinat0r.com/2018/12/09/secure-data-access-with-identityserver4-and-xamarin-forms and created xamarin forms mobile app with authentication on IS4. I set an access token lifetime to few minutes. After access token expires, as excepted, applciation is no more able to access authorized endpoints. I have an refresh_token but i dont't know how to use it to obtain a new access token from identity server.
Client specified in is4 configuration:
new Client()
{
ClientId = "xamarin-client",
ClientName = "Xamarin client",
AllowedGrantTypes = { "authorization_code" },
AllowedScopes = {"openid", "profile", "values-api" },
AllowAccessTokensViaBrowser = true,
AllowOfflineAccess = true,
AlwaysIncludeUserClaimsInIdToken = true,
RequirePkce = true,
RequireClientSecret = false,
RedirectUris = { "https://iglooidentityserver.azurewebsites.net/grants" },
AccessTokenLifetime = 180,
}
Authenticator i've used:
var oAuth = new OAuth2AuthenticatorEx(
"xamarin-client",
"offline_access values-api",
new Uri("https://iglooidentityserver.azurewebsites.net/connect/authorize"),
new Uri("https://iglooidentityserver.azurewebsites.net/grants"))
{
AccessTokenUrl = new Uri("https://iglooidentityserver.azurewebsites.net/connect/token"),
ShouldEncounterOnPageLoading = false,
};
var presenter = new OAuthLoginPresenter();
presenter.Completed += Presenter_Completed;
presenter.Login(oAuth);
I handled this problem in an old project as follows, hope this helps you.
public async Task<string> GetAccessToken()
{
if ((_authService.AuthAccessTokenExpireIn - DateTime.Now).TotalMinutes < 10) {
var authResponse = await GetRefreshTokenAsync(_authService.AuthRefreshToken);
_authService.AuthAccessToken = authResponse.AccessToken;
_authService.AuthRefreshToken = authResponse.RefreshToken;
_authService.AuthAccessTokenExpireIn = authResponse.ExpiresIn;
}
return _authService.AuthAccessToken;
}
public async Task<UserToken> GetRefreshTokenAsync(string currentRefreshToken)
{
string data = string.Format("grant_type=refresh_token&client_id={0}&client_secret={1}&refresh_token={2}", GlobalSetting.Instance.ClientId, GlobalSetting.Instance.ClientSecret, refreshToken);
var token = await PostAsync<UserToken>(_httpClient,
GlobalSetting.Instance.TokenEndpoint,
data);
return token;
}
public async Task<UserToken> PostAsync<UserToken>(HttpClient httpClient, string uri, object data)
{
var content = new StringContent(JsonConvert.SerializeObject(data));
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
UserToken result = await Task.Run(() => JsonConvert.DeserializeObject<UserToken>(serialized, _serializerSettings));
return result;
}

Core 2.0 API Auth with JWT returns unauthorized

I'm trying to add Token Authentication with JWT to my .Net Core 2.0 app. I have a simple controller that returns a list of users for testing.
[Authorize]
[Route("api/[controller]")]
public class UsersController : Controller
{
...
[HttpGet]
[Route("api/Users/GetUsers")]
public IEnumerable<ApplicationUser> GetUsers()
{
return _userManager.Users;
}
}
I have an API Controller for Token security. It has a login method which returns a Token string result.
[HttpPost(nameof(Login))]
public async Task<IActionResult> Login([FromBody] LoginResource resource)
{
if (resource == null)
return BadRequest("Login resource must be asssigned");
var user = await _userManager.FindByEmailAsync(resource.Email);
if (user == null || (!(await _signInManager.PasswordSignInAsync(user, resource.Password, false, false)).Succeeded))
return BadRequest("Invalid credentials");
string result = GenerateToken(user.UserName, resource.Email);
// Token is created, we can sign out
await _signInManager.SignOutAsync();
return Ok(result);
}
private string GenerateToken(string username, string email)
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Email, email),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds().ToString()),
};
var token = new JwtSecurityToken(
new JwtHeader(new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
SecurityAlgorithms.HmacSha256)),
new JwtPayload(claims));
return new JwtSecurityTokenHandler().WriteToken(token);
}
I have a small console app just for testing the API. When I attempt to Get the Users using the jwt. I receive an immediate "unauthorized". If I remove the "[Authorize]" from the users Controller... success. It appears that my header Authorization is not recognized, but not sure why.
private static async Task<String> GetUsers(String jwt)
{
var url = "https://localhost:44300/";
var apiUrl = $"/api/Users/";
using (var client = new HttpClient() { BaseAddress = new Uri(url) })
{
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {jwt}");
using (var response = await client.GetAsync(apiUrl))
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
return await response.Content.ReadAsStringAsync();
else return null;
}
}
}
I'm basing my attempts on the article here ... some of which might be slightly out of date.
Update - Excerpt of Startup.cs
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Jwt";
options.DefaultChallengeScheme = "Jwt";
}).AddJwtBearer("Jwt", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
//ValidAudience = "the audience you want to validate",
ValidateIssuer = false,
//ValidIssuer = "the isser you want to validate",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
ValidateLifetime = true, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date
};
});
Configure...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//app.UseJwtBearerAuthentication(
// new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions
// );
}
Solution:
This line was escaping the token therefore causing it to be invalid when passed in the next request:
var result = await response.Content.ReadAsStringAsync();
Replaced with:
var result = await response.Content.ReadAsAsync<string>();
Note: To use this ext method I had to "install-package Microsoft.AspNet.WebApi.Client"
I used JWT authentication in my one of project. I would like to show my implementation, maybe this will help you. But probably you forget to add UseAuthentication(); into configure method in startup class.
startup.cs
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc();
}
public void ConfigureServices(IServiceCollection services)
{
var appSettings = Configuration.GetSection("AppSettings");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}
)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = appSettings["JwtAudience"],
ValidateIssuer = true,
ValidIssuer = appSettings["JwtIssuer"],
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings["JwtSigningKey"]))
};
});
}
generateToken method
private string GenerateToken(string email)
{
SecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Value.JwtSigningKey));
var token = new JwtSecurityToken(
issuer: _appSettings.Value.JwtIssuer,
audience: _appSettings.Value.JwtAudience,
claims: new[]
{
new Claim(JwtRegisteredClaimNames.UniqueName, email),
new Claim(JwtRegisteredClaimNames.Email, email),
new Claim(JwtRegisteredClaimNames.NameId, Guid.NewGuid().ToString())
},
expires: DateTime.Now.AddMinutes(_appSettings.Value.JwtExpireMinute),
signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
I had created a nuget package NetCore.Jwt to simplify this process recently. I didn't find it worth writing all the code each time you needed a Jwt, when you can handle cookies simply with the function SignInAsync. However, if you prefer the manual way, Celal's answer is a clear and straightforward guide for this process.
Alternatively, you can install NetCore.Jwt and use the following in your startup:
services.AddAuthentication(NetCoreJwtDefaults.SchemeName)
.AddNetCoreJwt(options =>
{
// configure your token options such as secret, expiry, and issuer here
});
In your Login function, you can use the extension function for HttpContext
string token = HttpContext.GenerateBearerToken( new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Email, email),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds().ToString()),
});
In your program.cs dont forget to have this code (and in order) :
app.UseAuthentication();
app.UseAuthorization();

Resources