To grant or revoke access to my webapis, I use OAuth password- and tokenrefreshworkflow.
If I understand everything correctly the workflow should be something like this:
Authenticate with username / password / client id
Retrieve accestoken, refreshtoken and expire date
Start timeout in client to refresh your token after expired token time
Go on with bullet 2 -> and so on..
The progress above works fine so far. My problem is, that I don't get the expire time out of the users principle after the authentication request. So if I work with stateles webclients, I need to renew my token every request to retrieve a new expire date, even if the users token is valid :/
What I want is something like a /api/session/information service, that provides general information about the current session of an authenticated user.
How do I retrieve my expire date =)
[HttpGet]
[ActionName("information")]
public HttpResponseMessage Information(BaseRequest request)
{
var p = Request.GetRequestContext().Principal;
/* here i need help =) */
}
Just to expand on Henrik N.'s answer a little. If you're in C# then you can use JWTSecurityTokenHandler within System.IdentityModel.Tokens.Jwt (Nuget: Install-Package System.IdentityModel.Tokens.Jwt) to read the token and the resulting JwtSecurityToken object gives you some handy properties, one of which is ValidTo which converts the exp claim into a DateTime object for you E.g.:
var tokenString = GetTokenString(); // Arbitrary method to get the token
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadToken(tokenString) as JwtSecurityToken;
var tokenExpiryDate = token.ValidTo;
// If there is no valid `exp` claim then `ValidTo` returns DateTime.MinValue
if(tokenExpiryDate == DateTime.MinValue) throw new Exception("Could not get exp claim from token");
// If the token is in the past then you can't use it
if(tokenExpiryDate < DateTime.UtcNow) throw new Exception($"Token expired on: {tokenExpiryDate}");
// Token is valid
Your access token (JWT?) should contain an expiry claim. In JWT it is "exp", which shows the number of seconds since 1970-1-1. In javascript you can get a date from this like this:
new Date(<exp> * 1000);
In .Net / C# you would be able to do the same:
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return epoch.AddSeconds(<exp>);
Is that what you are looking for? Otherwise let me know. Happy to help :-)
You can use DateTimeOffset.FromUnixTimeSeconds:
var jwtExpValue = long.Parse(principal.Claims.FirstOrDefault(x => x.Type == "exp").Value);
DateTime expirationTime = DateTimeOffset.FromUnixTimeSeconds(jwtExpValue).DateTime;
Related
I'm struggling with a problem of how to update the users Access/Id token on expiry I know how to get new tokens, i'm trying to work out how to update the users current/expired token once I have the updated ones? following code works fine and the response returns new access/id tokens, a null session (which is odd). I have tried GetUserReequest using the currrent access token then updating it, to no effect. Documentation is a bit sparse on methods that expose access token also
public async Task RefreshTokens(string token)
{
var request = new AdminInitiateAuthRequest
{
UserPoolId = userpool,
ClientId = clientId,
AuthFlow = AuthFlowType.REFRESH_TOKEN_AUTH,
AuthParameters =
{
{"REFRESH_TOKEN", token},
{"SECRET_HASH", secret}
}
};
var response = await _cognitoIdentityProvider.AdminInitiateAuthAsync(request);
//Do something here to update users tokens
}
Have tried to set the Expiry time for the Email conformation link which is sending after user registration, the link token should expire after 10 mins. have used the code but even after 10 mins user can still access the link, here is my code,
var userManager = GetUserManager();
userManager.UserTokenProvider = new DataProtectorTokenProvider<User,int(dataProtectionProvider.Create("ConfirmEmail"))
{
TokenLifespan = TimeSpan.FromMinutes(10)
};
The first thing that comes to mind is that there is an error in your syntax.
userManager.UserTokenProvider =
new DataProtectorTokenProvider<User,int(dataProtectionProvider.Create("ConfirmEmail"))
is missing a closing bracket.
userManager.UserTokenProvider = here v
new DataProtectorTokenProvider<User,int>(dataProtectionProvider.Create("ConfirmEmail"))
then you have to call
userManager.UserTokenProvider.TokenLifespan = TimeSpan.FromMinutes(10);
The way you are doing it does not modify the TokenLifespan of the UserTokenProvider that is binded to your UserManager but some other attribute.
We're working on a new ASP.NET MVC 4.1 app. We're hooking up the ASP.NET Identity stuff, and we're struggling with tokens for password reset and new invite. I can't seem to find a way to set the expiration time for the tokens that are generated, and it seems to be set at around 10 mins by default. We're using a EmailTokenProvider as the user token provider, because it seems to work well with the security stamp on the user.
How can we set the expiration for the tokens - ideally we'd like to set it differently for the invite Vs the reset password tokens.
Our user manager looks like this:
var manager = new UserManager<User, long>(new UserStore(new UserRepository()));
manager.UserValidator = new UserValidator<User, long>(manager) {AllowOnlyAlphanumericUserNames = false, RequireUniqueEmail = true};
manager.UserTokenProvider = new EmailTokenProvider<User, long>();
When a user requests a reset password link we call
var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id); to get the token, and pass that on to the user.
When a user is invited, we call:
var token = await _userManager.GenerateUserTokenAsync("FirstLogin", user.Id);
to get the token, and send.
The default implementations of the token providers in 2.0 don't allow you to change the token expiration, this is something we are considering for identity 3.0.
If I understand your question well, following could help:
private static string CalculateToken(User user)
{
byte[] time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
byte[] key = BitConverter.GetBytes(user.ID);
string token = Convert.ToBase64String(time.Concat(key).ToArray());
return token;
}
And then:
DateTime time = DateTime.FromBinary(BitConverter.ToInt64(data, 0));
int id = BitConverter.ToInt32(data, 8);
if (time< DateTime.UtcNow.AddMinutes(-10) || user.ID != id)
{
//too old or IDs not matching
}
I`m a little bit confused about the Facebook Graph API.
First, I created an app on the developers page and then I autorized my app with a URL like this:
www.facebook.com/dialog/oauth?client_id=MY_CLIENT_ID&redirect_uri=http://www.facebook.com/connect/login_success.html&type=user_agent
Ok... after this the page returned an URL like this one:
www.facebook.com/connect/login_success.html#access_token=ACCESS_TOKEN&expires_in=5171411&code=CODE
I realized that the ACCESS_TOKEN returned is always the same. So i used to search for users, like this:
graph.facebook.com/search?q=QUERY_SEARCH&type=user&access_token=ACCESS_TOKEN
I believe all the URLs above are correct.
My doubt is: i don't know how to use the long-live token (actually I dont even know if the returned token is a long-lived one). The same token is always returned for me when I use those URLs, so I always use the same ACCESS_TOKEN.
But as i read on the Facebook Graph page, a token can't be active forever anymore... they now expire.
How do I know if i have a long live token or not? When a token expire how can I "refresh" it?
I was trying to follow the documentation but I`m totally lost...
developers.facebook.com/roadmap/offline-access-removal/
This page says that exists an "deprecate offline_acess" on the advanced settings menu... but it doens't!
So... i don't know how to manage tokens when they expire or how to know if i`m using a long-lived token
I think the general idea is that your access token will last a month or so, and when it stops working you need to request a new one.
I have a method like this to fetch a new one:
public static class GraphApiRequestProcessor
{
public static string GetNewAccessToken( CancellationToken cancellationToken )
{
const string tokenUrlPattern = #"https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&grant_type=client_credentials";
string tokenUrl = string.Format( tokenUrlPattern, Settings.FacebookAppId, Settings.FacebookAppSecret );
using( var client = new WebClient() )
{
// allows cancellation while executing request
using( cancellationToken.Register( client.CancelAsync ) )
{
using( var data = client.OpenRead( tokenUrl ) )
{
using( var reader = new StreamReader( data ) )
{
string response = reader.ReadToEnd();
int index = response.IndexOf( "=", StringComparison.InvariantCultureIgnoreCase );
string code = response.Substring( index + 1 );
return code;
}
}
}
}
}
}
You can check when your access token is going to expire on Access Token Debugger.
You can get long-lived access tokens by using this api where you need to enter the short lived access token.
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
where this will return the new long-lived access token. (which will have an expiry period of 2 months.)
I'm wondering if anyone has solved managing the expiration of Google OAuth2 tokens?
The example below is a REST call to get a list of calendars with a valid token. If the token is expired, I will get a 401 response and need to refresh the token using the 'refresh token' stored in my database. I'm wondering if anyone has a strategy around this for their web application?
var httpContent = new HttpRequestMessage(HttpMethod.Get, "https://www.googleapis.com/calendar/v3/users/me/calendarList");
httpContent.Headers.Add("Authorization", "OAuth " + token);
responseBody = client.SendAsync(httpContent).Result.EnsureSuccessStatusCode().Content.ReadAsStringAsync().Result;
I've thought about refreshing automatically upon user logging in and saving the refresh token encrypted in session, but not sure if there are any better strategies.
//Notes
Strategy Options:
1 - Upon Logging a user in, refresh all OAuth tokens for user. This works assuming that a session will never last longer than a token timeout period. (may not be reliable for all OAuth servers).
2 - When refreshing, use token expiration to record expiration date/time in database. Before calling any API, check if token needs to be refreshed. (still need to account for edge case where token expires unexpectedly outside of normal expiration schedule)
3 - Catch the response status of the call and check for 401s. If receive a 401, refresh the token and try again. This could be a fail-over for both Options 1/2. Code for this example would be here:
var restClient = new RestClient();
var request = new RestRequest("https://www.googleapis.com/calendar/v3/users/me/calendarList", Method.GET);
request.AddHeader("Authorization", "OAuth " + token);
// execute the request
var response = restClient.Execute(request);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var newToken = RefreshGoogleToken(token);
request = new RestRequest("https://www.googleapis.com/calendar/v3/users/me/calendarList", Method.GET);
request.AddHeader("Authorization", "OAuth " + newToken);
// execute the request
response = restClient.Execute(request);
}
var content = response.Content; // raw content as string
dynamic responseJson = JsonValue.Parse(content);
var calendarList = new List<GoogleCalendar>();
foreach (var item in responseJson.items)
{
var calendar = new GoogleCalendar { Kind = item.kind, Etag = item.etag, Id = item.id, Title = item.summary, Description = item.description, Location = item.location, Timezone = item.timeZone, SummaryOverride = item.summaryOverride, ColorId = item.colorId, AccessRole = item.accessRole };
if (item.defaultReminders != null)
{
calendar.DefaultReminders = new List<GoogleCalendarReminder>();
foreach (var reminder in item.defaultReminders)
{
var rem = new GoogleCalendarReminder { Method = reminder.method, Minutes = reminder.minutes };
calendar.DefaultReminders.Add(rem);
}
}
calendarList.Add(calendar);
}
return calendarList;
}
If the expiration time is known you can keep track of your token's expiration time and anticipate that it has already expired, then just do the refresh at that time.
I would imagine you could also simply respond to the 401 errors with a refresh as well, and even as a fail safe if the expiration tracking happens to fail (just be sure to add code to avoid getting stuck in a loop).