I got this sort of thing in oauth2
{
"token_type": "Bearer",
"expires_in": 43200,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjExMzk2NGY3MDZiNGM0ZTJhYTFiM2M4NGQ1Y2YwYWRhMjA4OWIwNDc2ZjM3NjlhN2I1ZTBmZDNkN2YyM2IxYmUxNWE0N2Y0YTU4YmYzMzE1In0.eyJhdWQiOiIxNDQiLCJqdGkiOiIxMTM5NjRmNzA2YjRjNGUyYWExYjNjODRkNWNmMGFkYTIwODliMDQ3NmYzNzY5YTdiNWUwZmQzZDdmMjNiMWJlMTVhNDdmNGE1OGJmMzMxNSIsImlhdCI6MTU1NDc4NzQ3MSwibmJmIjoxNTU0Nzg3NDcxLCJleHAiOjE1NTQ4MzA2NzEsInN1YiI6IjMxOTgwMSIsInNjb3BlcyI6WyJ0cmFkZSIsInByb2ZpbGUiXX0.blR3PYblZtYvd0VOnaePaUFu_dkE2yhbGgBLTHqXGuvaMC-57OhXaWNxLLjLzlHrL1bTJcMUYfRrBNhbpiLe9ln7FofuoRUfSm0IhBuvgX-5Le1k0KLd-1JdFW8Y1jIOYm8jGT7WdBRD3ykPoWAKcp_iuXgv2Ci_beii6W6ANX4BWShpekBuUfcKQoiXLNpmDWNt8SfbF4MDjDCs-Z81HAtzFr4P7lmMFugkqODOgVYg9VEhAdXWG56K8uTBmdc0v7w1Ahy7DE_wWzOMOA6JPq6qUvoIuz51I7LCLLhXUNeLXpELuXzREKqSbgZJb2KaZ67vbFYEM4dt8s67SKWfMv5KK7OPY6v0zB529m3r5H0wfvNOH7PNw782q9AmOSZc62yXCKijBNP-7XKsrzbK2LneVn8z_wDLJ3xjs7frppkQ0f-YAk3xmg8JJnPWDleLm4FUx32F2_MwPlN227ThgASi2N8jtRpkUgNFnOkBScDhEwpT5zqJK9H09pTkUIWdDCB7H5er0HR9qBK509qif4b9UGl1OHMnjQKyFQT36c5nGJCG05E0IRH8Mbax37Wv94Eb_53rpJwEWv_XkuT-N_wPEwf92VvwCUhoHxTK9a-p78NfCuroRQWARk4gdhZ9i_rCgQQEzlEk9EsiFl_bTcBgT7bU",
"refresh_token": "def5020046875925bb9c0ba2ead1954836339f8f31d5a45103603b857567096da0eb820114a75a6ed3db9eb301e3389d284a696a3cb21ea0df9eebdca2052e8b8120423bb2d4b063651aeff7ac7623ea3beea2f3f21b5127792daf6f71f4d23980ca140f875ec5607f63deeac8696128ea2918a473486c0c4223a088f385046b84e6f8edd17cb459d5cdc66d856ee42b2ce3f90f3829a104735372ac14eae3ccff71dde4552b9ad46df7380870b5cd3bcb8d6ca7f16484a2d5b3b26efcebf7b2e5221ed16620445099ee4b0239fd82c8e7f37262883d57fc6545ab31f9e52dc4fc4de70235d5121f7222f8066bfdc9945aa9ac5940c6e2fdc9daa6623a9f2b6e3f41aa47698ec1008514878494fef9932b317f42873af44d5dbcdd19958c2fc3835a820d09e5aa6a6ae3bef6592812a698b2547f0cca5e9f8ac38014b6651be46a098374f92bc35fcebc373f17ab70fd20cf1147bf11e2093e9908516717a3902d2d9efc7f06e917fb67dca2bca4f0c25ed7",
"expire": 1554787516031
}
I changed the access token and refresh token a bit
Now there is expires_in 43200. Is that second or milisecond or years or what?
I used this code
Public Shared Function currentTimeStamp() As Int64
Dim utime = (DateTime.UtcNow - unixEpoch()).TotalMilliseconds
Return CLng(utime)
End Function
Dim jtoken1 = JToken.Parse(jtoken2)
Dim accesstoken = jtoken1.Item("access_token")
Dim expirytime = jtoken1.Item("expires_in")
Dim currentTimeStamp = jsonHelper.currentTimeStamp()
Dim timeexpire = currentTimeStamp + CLng(expirytime)
Dim refreshtoken = jtoken1.Item("refresh_token").ToString
jtoken1.Item("expire") = timeexpire
That seems to be a bit wrong.
Is there any documentation that can clearly says it's seconds or milliseconds?
The expires_in is value in seconds. See this section of the specification.
Related
I am writing a Flutter/Dart application and am getting a JWT back from an auth server that has some claims I need to use. I have looked at various (4 so far) Dart JWT libraries -- but all are either too old and no longer work with Dart 2, etc. or they need the secret to decode the JWT which makes no sense and isn't correct (or possible since I have no access ).
So -- how can one get a JWT and get the claims from it within a "modern" Dart/Flutter application?
JWT tokens are just base64 encoded JSON strings (3 of them, separated by dots):
import 'dart:convert';
Map<String, dynamic> parseJwt(String token) {
final parts = token.split('.');
if (parts.length != 3) {
throw Exception('invalid token');
}
final payload = _decodeBase64(parts[1]);
final payloadMap = json.decode(payload);
if (payloadMap is! Map<String, dynamic>) {
throw Exception('invalid payload');
}
return payloadMap;
}
String _decodeBase64(String str) {
String output = str.replaceAll('-', '+').replaceAll('_', '/');
switch (output.length % 4) {
case 0:
break;
case 2:
output += '==';
break;
case 3:
output += '=';
break;
default:
throw Exception('Illegal base64url string!"');
}
return utf8.decode(base64Url.decode(output));
}
Use 'base64Url.normalize()' function.
That's what _decodeBase64() does from the answer above!
String getJsonFromJWT(String splittedToken){
String normalizedSource = base64Url.normalize(encodedStr);
return utf8.decode(base64Url.decode(normalizedSource));
}
As of this writing, the jaguar_jwt package is being actively maintained. Although it is not clearly documented, it does have a public method that will decode Base64Url encoding. It does basically the same thing as the accepted answer.
//import 'package:jaguar_jwt/jaguar_jwt.dart';
final String token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTQ4MjAxNjIsImlhdCI6MTU1NDc3Njk2MiwiaXNzIjoiU3VyYWdjaCIsInN1YiI6IjQifQ.bg5B_k9WCmxiu2epuZo_Tpt_KZC4N9ve_2GEdrulcXM';
final parts = token.split('.');
final payload = parts[1];
final String decoded = B64urlEncRfc7515.decodeUtf8(payload);
This gives a JSON string, which for this particular example is:
{
"exp":1554820162,
"iat":1554776962,
"iss":"Suragch",
"sub":"4"
}
See also:
JWT: The Complete Guide to JSON Web Tokens
String based data encoding: Base64 vs Base64url
you can use jwt_decoder package to decode and/or check if you token is expired
//to get claims from your token
main () {
String yourToken = "Your JWT";
Map<String, dynamic> decodedToken =
JwtDecoder.decode(yourToken);
/*
If the token has a valid format, you will get a
Map<String,dynamic> Your decoded token can look like:
{
"sub": "1234567890",
"name": "Gustavo",
"iat": 1516239022,
"exp": 1516239022,
"randomKey": "something else"
}
*/
}
//check if your token is expired
main () {
String yourToken = "Your JWT";
bool hasExpired = JwtDecoder.isExpired(yourToken);
// You will get a true / false response
// true: if the token is already expired
// false: if the token is not expired
}
you can get your token expiration date using
main () {
String yourToken = "Your JWT";
DateTime expirationDate = JwtDecoder.getExpirationDate(token);
// 2025-01-13 13:04:18.000
print(expirationDate);
}
you can also find out how old your token is
// Token payload must include an 'iat' field
main () {
String yourToken = "Your JWT";
Duration tokenTime = JwtDecoder.getTokenTime(token);
// 15
print(tokenTime.inDays);
}
to learn more about what JWT Decoder can do, visit their package documentation page
you can decode JWT base64 by seperate first part of it that contains "."
String decodeUserData(String code) {
String normalizedSource = base64Url.normalize(code.split(".")[1]);
return utf8.decode(base64Url.decode(normalizedSource));
}
Following https://developers.google.com/identity/sign-in/web/server-side-flow After getting the authorization code from JavaScript, and passing it to the server side, we indeed get an access token (and an ID token), but not the required refresh token.
There are many posts around but could not solve it yet.
Any suggestion how to get the refresh token?
thanks!
private String getResponseToken(GoogleClientSecrets clientSecrets,
String authCode) throws IOException {
try {
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
// "https://accounts.google.com/o/oauth2/token",
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
authCode, //NOTE: was received from JavaScript client
"postmessage" //TODO: what's this?
).execute();
String accessToken = tokenResponse.getAccessToken();
String idToken = tokenResponse.getIdToken();
//TODO: not getting a refresh token... why?!
String refreshToken = tokenResponse.getRefreshToken();
Boolean hasRefreshToken = new Boolean(!(refreshToken == null));
LOGGER.warn("received refresh token: {}", hasRefreshToken);
LOGGER.debug("accessToken: {}, refreshToken: {}, idToken: {}", accessToken, refreshToken, idToken);
return accessToken;
}catch (TokenResponseException tre){...}
Gmail API only gives the refresh token the first time you ask for the users permission. (At least this is what happens to me).
Go to: https://myaccount.google.com/permissions?pli=1, remove the authorization to your app and run your code. You should receive the refresh token.
you should add the
AccessType = "offline"
You need to call the function
new GoogleAuthorizationCodeRequestUrl(...).setAccessType("offline")
or another syntax:
var authReq = new GoogleAuthorizationCodeRequestUrl(new Uri(GoogleAuthConsts.AuthorizationUrl)) {
RedirectUri = Callback,
ClientId = ClientId,
AccessType = "offline",
Scope = string.Join(" ", new[] { Scopes... }),
ApprovalPrompt = "force"
};
in Fiddler you should see the following request:
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/webmasters&redirect_uri=http://mywebsite.com/google/scapi/callback/&response_type=code&client_id=xxx&access_type=offline
see also here
More details about setAccessType can be found here
after finding how to use the Google APIs at the backend (documentation is somewhat partial..), the issue was fixed at the FrontEnd side by tweaking a parameter:
grantOfflineAccess({
- prompt: 'select_account'
+ prompt: 'consent'
HTH
In below code I can retrieve refresh token successfully from email#company.com email addresses. However, when I try to login with email#outlook.com it doesn't give the refresh token instead it returns this response.
Response:
{
"error": "invalid_grant",
"error_description": "AADSTS70000: The provided value for the 'code' parameter is not valid. The code has expired.\r\nTrace ID: ...\r\nCorrelation ID: ...\r\nTimestamp: 2016-05-19 10:13:05Z",
"error_codes": [
70000
],
"timestamp": "2016-05-19 10:13:05Z",
"trace_id": "8cceb393-....",
"correlation_id": "5227de8...."
}
Code:
private async Task<string> GetRefreshRoken(string authCode, string onSuccessRedirectUri) {
var client = new HttpClient();
var parameters = new Dictionary<string, string>
{
{"client_id", _clientId},
{"client_secret", _clientSecret},
{"code",authCode }, // what retreived from //https://login.microsoftonline.com/common with authroization.
{"redirect_uri", onSuccessRedirectUri}, //http://localhost:27592/Home/Authorize
{"grant_type","authorization_code" }
};
var content = new FormUrlEncodedContent(parameters);
var response = await client.PostAsync("https://login.microsoftonline.com/common/oauth2/v2.0/token", content);
var tokensJsonString = await response.Content.ReadAsStringAsync();
dynamic token = Newtonsoft.Json.JsonConvert.DeserializeObject(tokensJsonString);
return token.refresh_token;
}
So I had googled with the error number and found http://www.matvelloso.com/2015/01/30/troubleshooting-common-azure-active-directory-errors/ page where the error describes:
Then I had changed my redirecting url to "http://localhost:27592/Home/Authorize/". Since I am using this https://dev.outlook.com/restapi/tutorial/dotnet tutorial as a reference , now I cannot login with any other account.
Is there any good approach to retrieve refresh tokens for outlook account?
For windows live id account, you will get error "The provided value for the 'code' parameter is not valid. The code has expired." when using the authorization code twice.
The correct way to refresh the token is using refresh token (v2.0 token reference > Refresh Token).
First, ensure you have declare the scope "offline_access".
Then, you will get the access_token when acquire the token using grant_type=code (the first time you acquire the token).
Next, you need to use grant_type=refresh_token to refresh your access token.
I am using Postman to test OAuth 2 from a vanilla AEM install.
Postman can successfully obtain the authorization code from /oauth/authorize after I grant access:
But when it tries to use the code to obtain a token from /oauth/token it receives the following response:
HTTP ERROR: 403 Problem accessing /oauth/token. Reason: Forbidden
Powered by Jetty://
Looking in Fiddler it is doing a POST to /oauth/token with the following Name/Values in the body:
client_id: Client ID from /libs/granite/oauth/content/client.html
client_secret:
Client Secret from /libs/granite/oauth/content/client.html
redirect_uri: https://www.getpostman.com/oauth2/callback
grant_type: authorization_code
code: Code returned from previous request to oauth/authorize
Am I missing something?
Would help if you can list some code snippets on how you are building the url and fetching the token.
Here's an example of how we've implemented very similar to what you are trying to do, maybe it'll help.
Define a service like below (snippet) and define the values (host, url, etc) in OSGI (or you can also hard code them for testing purposes)
#Service(value = OauthAuthentication.class)
#Component(immediate = true, label = "My Oauth Authentication", description = "My Oauth Authentication", policy = ConfigurationPolicy.REQUIRE, metatype = true)
#Properties({
#Property(name = Constants.SERVICE_VENDOR, value = "ABC"),
#Property(name = "service.oauth.host", value = "", label = "Oauth Host", description = "Oauth Athentication Server"),
#Property(name = "service.oauth.url", value = "/service/oauth/token", label = "Oauth URL", description = "Oauth Authentication URL relative to the host"),
#Property(name = "service.oauth.clientid", value = "", label = "Oauth Client ID", description = "Oauth client ID to use in the authentication procedure"),
#Property(name = "service.oauth.clientsecret", value = "", label = "Oauth Client Secret", description = "Oauth client secret to use in the authentication procedure"),
#Property(name = "service.oauth.granttype", value = "", label = "Oauth Grant Type", description = "Oauth grant type") })
public class OauthAuthentication {
...
#Activate
private void activate(ComponentContext context) {
Dictionary<String, Object> properties = context.getProperties();
host = OsgiUtil.toString(properties, PROPERTY_SERVICE_OAUTH_HOST,new String());
// Similarly get all values
url =
clientID =
clientSecret =
grantType =
authType = "Basic" + " "+ Base64.encode(new String(clientID + ":" + clientSecret));
}
public static void getAuthorizationToken(
try {
UserManager userManager = resourceResolver.adaptTo(UserManager.class);
Session session = resourceResolver.adaptTo(Session.class);
// Getting the current user
Authorizable auth = userManager.getAuthorizable(session.getUserID());
user = auth.getID();
password = ...
...
...
String serviceURL = (host.startsWith("http") ? "": protocol + "://") + host + url;
httpclient = HttpClients.custom().build();
HttpPost httppost = new HttpPost(serviceURL);
// set params
ArrayList<BasicNameValuePair> formparams = new ArrayList<BasicNameValuePair>();
formparams.add(new BasicNameValuePair("username", user));
formparams.add(new BasicNameValuePair("password", password));
formparams.add(new BasicNameValuePair("client_id", clientID));
formparams.add(new BasicNameValuePair("client_secret",clientSecret));
formparams.add(new BasicNameValuePair("grant_type",grantType));
UrlEncodedFormEntity postEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(postEntity);
// set header
httppost.addHeader("Authorization", authType);
response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (response.getStatusLine().getStatusCode() == 200) {
if (entity != null) {
object = new JSONObject(EntityUtils.toString(entity));
}
if (object != null) {
accessToken = object.getString("access_token");
////
}
}
}
I found the answer myself and thought I'd share the process I went through as well as the answer because it might help other people new to AEM.
How to find the cause of the error:
Go to CRXDE Lite.
Select console.
Then deselect the stop button to allow new console logs to appear (this is very counter-intuitive to me).
From here I was able to see the cause of the issue:
org.apache.sling.security.impl.ReferrerFilter Rejected empty referrer header for POST request to /oauth/token
Because postman does not place a referrer in the request header I had to tell Apache Sling to allow empty request headers.
To do this:
Go to /system/console/configMgr
Open the Apache Sling Referrer Filter Config
Select the Allow Empty check box
Good way to allow this to list the allowed hosts, otherwise this is against best practices for AEM security checklist.
Its fine for development environment not for production.
Using MVC.
Function Login(ByVal token As String) As ActionResult
Using client As New WebClient
Try
Dim jsonResponse As String = client.DownloadString(URL & "/Getuser&token=" & token)
Dim obj As UserInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(Of UserInfo)(jsonResponse)
Response.Cookies.Add(New HttpCookie("token", token))
Response.Cookies.Add(New HttpCookie("user_id", obj.id))
Return Json(obj)
Catch ex As WebException
Return Content("ERROR")
Catch ex As Exception
Return Content("ERROR")
End Try
End Using
End Function
I am sending a token to this function.
Then Using this token to get the User Info from a certain API
Then Storing this token in a HttpCookie
All this has been working fine for almost a month,
Until it stopped working.
When I debugged, token had a value, and it stored it in the HttpCookie, but when I called Request.Cookies("token").Value it returned ''
Any help would be appreciated.
I did a trace on the Token..
I am writing the parameter "token" in a file before storing it in the cookie.
then I am writing the cookie Request.Cookies("token").Value in a file,
Function Login(ByVal token As String) As ActionResult
WriteToFile("TOKEN RECEIVED = ", token)
Using client As New WebClient
Try
Dim jsonResponse As String = client.DownloadString(URL & "/Getuser&token=" & token)
Dim obj As UserInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(Of UserInfo)(jsonResponse)
Response.Cookies.Add(New HttpCookie("token", token))
Response.Cookies.Add(New HttpCookie("user_id", obj.id))
WriteToFile("TOKEN COOKIE = ", Request.Cookies("token").Value)
Return Json(obj)
Catch ex As WebException
Return Content("ERROR")
Catch ex As Exception
Return Content("ERROR")
End Try
End Using
End Function
it returns the following:
TOKEN RECEIVED = X132WEeRT3AASDV
TOKEN COOKIE =
When I try to write both Request and Response Cookies:
WriteToFile("TOKEN COOKIE = ", Request.Cookies("token").Value)
WriteToFile("TOKEN COOKIE = ", Response.Cookies("token").Value)
Request.Cookies("token").Value Returns Empty String
Response.Cookies("token").Value Returns Actual Value
Maybe your cookie just expire after one month? When using cookies don't forget to set expiration date, and check web browser settings (example: if you are using "tor browser bundle" this can be an issue).
HttpCookie myCookie = new HttpCookie("UserSettings");
myCookie["Font"] = "Arial";
myCookie["Color"] = "Blue";
myCookie.Expires = DateTime.Now.AddDays(1d);
Response.Cookies.Add(myCookie);
https://msdn.microsoft.com/en-us/library/78c837bd%28v=vs.140%29.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
Make sure you access the cookies only during the actual Http request. Maybe you have changed the way (or place where) you call this function.
This is an old question on SO but for those interested, .NET team has created a new method to properly handle cookies.
This is available in .NET 4.7.1 and not earlier versions:
See under the section ASP.NET HttpCookie parsing here
https://blogs.msdn.microsoft.com/dotnet/2017/09/13/net-framework-4-7-1-asp-net-and-configuration-features/