I have a login method, which give me the access and refresh token.
[HttpGet]
[AllowAnonymous]
public IActionResult Login()
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Role, "Administrator")
};
var now = DateTime.UtcNow;
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.configuration["Key"])), SecurityAlgorithms.HmacSha256);
var accessToken = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
claims: claims,
notBefore: now,
expires: now.AddMinutes(10),
signingCredentials: signingCredentials));
var refreshToken = new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
notBefore: now,
expires: now.AddYears(2),
signingCredentials: signingCredentials));
return Ok(new JwtToken
{
AccessToken = accessToken,
RefreshToken = refreshToken
});
}
And I use the access token with Postman. In the headers:
Bearer eyJhbGciOiJIUzI1...
But after 10 minutes I can't use the API because the access token is rejected. How can I renew the access token with every request to the API (within these 10 minutes)?
You could set a variable or cookie with the expiry time of the token, then every request that is made you need to check if this expiry is in the past. If it is, you should be able to use the refresh token to get a new access token.
This will ensure you are not getting a token for each request but only when the token expires.
Related
I want an example code to allow users to login with their username and password in Azure AD. After successfully logging in, I want to get an Access Token
At the moment I have no connection with Azure AD, I hard-coded a user.
// POST api/values
[HttpPost, Route("login")]
public IActionResult Login([FromBody] LoginModel user)
{
if (user == null)
{
return BadRequest("Invalid client request");
}
if (user.UserName == "JO3434" && user.Password == "defDDMKJM")
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSMKLJMKey#345"));
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName)
};
var token = new JwtSecurityToken(
audience: "http://site.azurewebsites.net",
issuer: "http://site.azurewebsites.net",
claims: claims,
expires: DateTime.Now.AddMinutes(60),
signingCredentials: signinCredentials
);
var results = new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
};
return Ok(results);
}
else
{
return Unauthorized();
}
}
}
Without connection with Azure AD, you could not validate the user, also I think it is unnecessary to do that, if you get the token with AcquireTokenByUsernamePassword methond, it essentially uses the Azure AD ROPC flow, it will validate the user automatically for you, if the user is invalidated, it will give an error Error validating credentials due to invalid username or password.
I am using
var payload = new JwtPayload(issuer, audience, claim identities, DateTime.Now, DateTime.Now.AddMinutes(60), DateTime.Now);
to generate auth token having 60-minute expiring time but it is expiring before the expiring time
I use the following method to build a token in my asp.net core web api service:
private string BuildToken()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.Key));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
_jwtOptions.Issuer, // some issuer, e.x. you can specify your localhost
_jwtOptions.Issuer,
expires: DateTime.Now.AddMinutes(_jwtOptions.Expires), // int value
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
Perhaps this helps you somehow.
I'm trying to access the outlook 365 task api using a pre-defined microsoft account username and password which I will put in the config file.
Currently my app is redirecting to the microsoft login page using
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
And then I get the token under the current signed in user context.
string accessToken = await AuthProvider.Instance.GetUserAccessTokenAsync();
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer" + accessToken);
snippet of the GetUserAccessTokenAsync()
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
HttpContextBase httpContextBase = HttpContext.Current.GetOwinContext().Environment["System.Web.HttpContextBase"] as HttpContextBase;
SessionTokenCache tokenCache = new SessionTokenCache(signedInUserID, httpContextBase);
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(SettingsHelper.Authority, tokenCache);
ClientCredential clientCredential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
string userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
UserIdentifier userId = new UserIdentifier(userObjectId, UserIdentifierType.UniqueId);
try
{
AuthenticationResult result = await authContext.AcquireTokenSilentAsync(SettingsHelper.OutlookResourceId, clientCredential, userId);
return result.AccessToken;
}
catch (AdalException ex)
{
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
throw new Exception(ex.Message);
}
But my goal is to remove this login and just use the fixed admin account to access the api.
is it possible to do get the token from a user credential which is different from the signed in one?
I'm trying to search for examples but I can't find anything that fits.
I'm quite new in using the API so i'm still learning. :)
Any ideas are very much appreciated.
Thank you in advance.
The first option (and perhaps the more secure option) would be to have a small onboarding flow through which you login with the admin, acquire a refresh token for them, and store that somewhere secure.
Then you can use the refresh token whenever you need an access token.
Though you have to remember to refresh the refresh token as well as it expires too.
But you get a new refresh token every time you acquire a token with one, so should not be a problem.
The greatest advantage this approach has is that it allows the admin account to have MFA etc., unlike the second option.
The second option is to use the Resource Owner Password Credentials Grant flow.
I believe ADAL.NET does not give you access to an overload of AcquireTokenAsync that allows doing that from a confidential client, so you will have to make the HTTP call manually to get the tokens.
Here is an example of ROPC:
string tokenUrl = $"https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/token";
var req = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
req.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["client_id"] = "23d3be1b-a671-4452-a928-78fb842cb969",
["client_secret"] = "REDACTED",
["resource"] = "https://graph.windows.net",
["username"] = "testuser#mytenant.onmicrosoft.com",
["password"] = "REDACTED"
});
using (var client = new HttpClient())
{
var res = await client.SendAsync(req);
string json = await res.Content.ReadAsStringAsync();
}
And the HTTP request:
POST https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: login.microsoftonline.com
Content-Length: 277
Expect: 100-continue
Connection: Keep-Alive
grant_type=password&client_id=23d3be1b-a671-4452-a928-78fb842cb969&client_secret=REDACTED&resource=https%3A%2F%2Fgraph.windows.net&username=testuser%40mytenant.onmicrosoft.com&password=REDACTED
The greatest downside of ROPC is that the user account cannot use MFA, nor can it be a federated user.
I would prefer the first option personally, but it has its downsides too.
Refresh tokens can become invalid for certain reasons, one of them being a password reset (though that would affect ROPC too).
I am very new to .NET / C#.
I am using jwt for means of authentication. I have this object:
var token = new JwtSecurityToken(
issuer: "example.com",
audience: "example.com",
claims: claims,
expires: DateTime.Now.AddDays(TOKEN_EXPIRY_DAYS),
signingCredentials: creds
);
However, I manually add a "created" property:
token.Payload["created"] = DateTimeOffset.Now.ToUnixTimeSeconds();
All I need to do is to write a test that checks to see if the token object has an attribute called "created".
I have tried something like this:
tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(
await response.Content.ReadAsStringAsync()
);
Assert.NotEmpty(tokenResponse.token["created"]);
But this fails. Any ideas?
Im trying to setup Token authentication with cookie authentication on same time in my application.
I created a MVC project in asp.net core 2.0, with individual user accounts to auth. Setup roles to the users too.
If i follow this tutorial of Shawn Wildermuth Two-AuthorizationSchemes-in-ASP-NET-Core-2
Everything works fine to get the Token of the registered user. But if i use the Role attribute on authorize [Authorize(Roles="Admin")] im getting a 403 response.
I think that is because the Token is not receiving the Role on auth.
How to setup this? Is any way to pass the Roles on the Token process?
To generate the token he is using this piece of code:
[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> GenerateToken([FromBody] LoginViewModel model) { if (ModelState.IsValid) {
var user = await _userManager.FindByEmailAsync(model.Email);
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
if (result.Succeeded)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
} }
return BadRequest("Could not create token"); }
You guys have any idea?
Thanks
If you add the following using and code, that should help.
using System.Security.Claims;
...
var userRoles = await _userManager.GetRolesAsync(user);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}.Union(userRoles.Select(m => new Claim(ClaimTypes.Role, m)));
You can see the Union that adds the roles in with the type of ClaimTypes.Role, this will enable them to be used in the AuthorizeAttribute
HTH