My access_token contains lot of ...
I need to manually copy only the relevant parts of the access token (without trailing ...) to follow up requests.
How do I parse this access token to variable in postman without trailing ..., because if I put
pm.environment.set("AccessToken", accessToken);
above line in Tests section in postman, then AccessToken variable
Ok I'm able to remove trailing ... from access token via following script in Tests part of request
var response = pm.response.json()
var accessToken = response.access_token;
while(accessToken.charAt(accessToken.length-1) == '.')
{
accessToken = accessToken.substr(0, accessToken.length-1);
}
pm.collectionVariables.set("AccessToken", accessToken);
#DalmTo
Here is how I did it. (I've used the existing collection with minor changes to the script - Sorry I forgot where I got script from)
There are 2 Request in total (only 1st time 2 request, after that you only need to perform 2nd request)
Pre-requisite step
create collection variable privateKey and set its value to your PRIVATE KEY from .json file that you got during Service Account key creation.
eg.
{-----BEGIN PRIVATE KEY-----*****(Complete PRIVATE Key excluding \n from it)****-----END PRIVATE KEY-----
}
Request 1 Script (you only need to run this script once)
GET http://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js
in this request put following script in Tests section to save jsrsasign-js as collection variable
pm.collectionVariables.set('jsrsasign-js', responseBody);
Request 2 Script (you run this request every time you need token)
POST https://oauth2.googleapis.com/token
Body
x-www-form-urlencoded
grand_type : urn:ietf:params:oauth:grant-type:jwt-bearer
assertion : {{jwt}}
Pre-request Script
var navigator = {};
var window = {};
eval(pm.collectionVariables.get("jsrsasign-js"));
var scope = pm.collectionVariables.get('scope');
var iss = pm.collectionVariables.get('iss');
var privateKey = pm.collectionVariables.get('privateKey');
const header = {"alg" : "RS256", "typ" : "JWT"};
const claimSet =
{
"iss":"service account email",
"sub":"user's email that you requesting token for",
"scope":"https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events" ,
"aud":"https://oauth2.googleapis.com/token",
"exp":KJUR.jws.IntDate.get("now + 1hour").toString(),
"iat": KJUR.jws.IntDate.get("now").toString()
}
console.log(`header: ${ JSON.stringify(header)}`);
console.log(`claim set: ${ JSON.stringify(claimSet) }`);
var jwt = KJUR.jws.JWS.sign(null, header, claimSet, privateKey);
console.log(jwt);
pm.collectionVariables.set('jwt', jwt);
In the above script, you can change the value of
"sub" : "User's email address" to impersonate that user and get token on their behalf. Also, change "Scope" : " to appropriate scope"
Now when you run the second Request You will get token in response
{
"access_token": "ya29.a0ARr*********",
"expires_in": 3599,
"token_type": "Bearer"
}
You can add this script in Tests section of 2nd Request to parse that token to the collection variable and use that variable for all subsequent requests for Google API
var response = pm.response.json()
var accessToken = response.access_token;
while(accessToken.charAt(accessToken.length-1) == '.')
{
accessToken = accessToken.substr(0, accessToken.length-1);
}
pm.collectionVariables.set("AccessToken", accessToken);
Subsequent requests
GET https://www.googleapis.com/calendar/v3/calendars/calendarID/events
Authorization
Type : Bearer Token
Token : {{AccessToken}}
Clarification
When I'm requesting as particular user's token with "sub": "user's email", Access token that I received does not have trailing .... in them.
Related
I'm allowing users logged in an external application to jump into our application with their access token through Keycloak's identity brokering and external to internal token exchange.
Now I'd like to establish an SSO session in an embedded JxBrowser in our application similar to a regular browser login flow, where three cookies are set in the browser: AUTH_SESSION, KEYCLOAK_SESSION(_LEGACY) and KEYCLOAK_IDENTITY(_LEGACY).
KEYCLOAK_IDENTITY contains a token of type Serialized-ID which looks somewhat similar to an ID token.
Is it possible to create the KEYCLOAK_IDENTITY cookie using the exchanged (internal) access and/or ID token and, provided that the other two cookies are correctly created as well, would this establish a valid SSO session?
Basically all I am missing is how I could obtain or create the Serialized-ID type token.
One way to achieve this:
Implement a custom endpoint following this example
Note that the provider works fine for me without registering it in standalone.xml, I'm just adding the JAR to the Keycloak Docker image.
Add a method that validates a given access token, looks up the user, gets the user session and sets the cookies in the response (most error handling omitted for brevity):
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("sso")
public Response sso(#Context final HttpRequest request) {
final HttpHeaders headers = request.getHttpHeaders();
final String authorization = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
final String[] value = authorization.split(" ");
final String accessToken = value[1];
final AccessToken token = Tokens.getAccessToken(accessToken, keycloakSession);
if (token == null) {
throw new ErrorResponseException(Errors.INVALID_TOKEN, "Invalid access token", Status.UNAUTHORIZED);
}
final RealmModel realm = keycloakSession.getContext().getRealm();
final UriInfo uriInfo = keycloakSession.getContext().getUri();
final ClientConnection clientConnection = keycloakSession.getContext().getConnection();
final UserModel user = keycloakSession.users().getUserById(token.getSubject(), realm);
final UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
AuthenticationManager.createLoginCookie(keycloakSession, realm, user, userSession, uriInfo, clientConnection);
return Response.noContent().build();
}
Disclaimer: I am not completely certain this implementation does not imply any security issues, but since Tokens.getAccessToken(accessToken, keycloakSession) does full validation of the access token, setting the cookies should only be possible with a valid access token.
For CORS, add:
#OPTIONS
#Produces(MediaType.APPLICATION_JSON)
#Path("sso")
public Response preflight(#Context final HttpRequest request) {
return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
.auth()
.preflight()
.allowedMethods("GET", "OPTIONS")
.build();
}
and in sso():
return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
.auth()
.allowedMethods("GET")
.allowedOrigins(token)
.build();
What I am uncertain about is why Firefox preflights the GET request, making it necessary to handle that.
I want to authenticate AAD users to access powerBi resources through MSAL by using application ID and secret. So i want to get the access token and cache it in SQL Db.
went through the documentation but it explains the scenario of using MSAL for sign-in.
also went through the tutorial
i was able to to do the necessary implementations to get the token.
how can i get the access token and cache it, in a scenario like this?
As indicated in other answers, caching tokens are useful in case when you have users signing in, as once the access token expires (typically after 1 hour), you don't want to keep prompting the users to re-authenticate.
So help with these scenarios, Azure AD issues a refresh token along with an access token that is used to fetch access tokens once they expire. Caching is required to cache these refresh tokens as they are valid for 90 days.
When an app signs as itself (and not signing in a user), the client credentials flow is used and it only needs the app id (clientId) and the credential (secret/certificate) to issue an access token. The MSAL library will automatically detect when the access token expires and will use the clientId/credential combination to automatically get a new access token. So caching is not necessary.
The sample you should be looking at is this one.
I'n not sure to understand, I hope these few lines of code will help you.
First, customize token cache serialization :
public class ClientApplicationBuilder
{
public static IConfidentialClientApplication Build()
{
IConfidentialClientApplication clientApplication =
ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithRedirectUri(RedirectUri)
.WithClientSecret(ClientSecret)
.Build();
clientApplication.UserTokenCache.SetBeforeAccessAsync(BeforeAccessNotification);
clientApplication.UserTokenCache.SetAfterAccessAsync(AfterAccessNotification);
return clientApplication;
}
private static async Task<byte[]> GetMsalV3StateAsync()
{
//TODO: Implement code to retrieve MsalV3 state from DB
}
private static async Task StoreMsalV3StateAsync(byte[] msalV3State)
{
//TODO: Implement code to persist MsalV3 state to DB
}
private static async Task BeforeAccessNotification(TokenCacheNotificationArgs args)
{
byte[] msalV3State = await GetMsalV3StateAsync();
args.TokenCache.DeserializeMsalV3(msalV3State);
}
private static async Task AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (args.HasStateChanged)
{
byte[] msalV3State = args.TokenCache.SerializeMsalV3();
await StoreMsalV3StateAsync(msalV3State);
}
}
}
Here's an example to acquire token (by Authorization Code) :
public class MsAccountController
: Controller
{
private readonly IConfidentialClientApplication _clientApplication;
public MsAccountController()
{
_clientApplication = ClientApplicationBuilder.Build();
}
[HttpGet]
public async Task<IActionResult> Index()
{
Uri authorizationRequestUrl = await _clientApplication.GetAuthorizationRequestUrl(ClientApplicationHelper.Scopes).ExecuteAsync();
string authorizationRequestUrlStr = authorizationRequestUrl.ToString();
return Redirect(authorizationRequestUrlStr);
}
[HttpGet]
public async Task<IActionResult> OAuth2Callback(string code, string state)
{
AuthenticationResult authenticationResult = await _clientApplication.AcquireTokenByAuthorizationCode(scopes, code).ExecuteAsync();
return Ok(authenticationResult);
}
}
Finally, acquire a token silently and use auth result for your API client :
public class TaskController
: Controller
{
private readonly IConfidentialClientApplication _clientApplication;
public TaskController()
{
_clientApplication = ClientApplicationBuilder.Build();
}
[HttpGet]
public async Task<IActionResult> Index()
{
IEnumerable<IAccount> accounts = await _clientApplication.GetAccountsAsync();
AuthenticationResult result = await _clientApplication.AcquireTokenSilent(ClientApplicationHelper.Scopes, accounts.FirstOrDefault()).ExecuteAsync();
//TODO: Create your API client using authentication result
}
}
Regards
You can cache the access token (actually, the library does this already), but it is valid for 1 hour only. So it makes no sense to save it in a database, because it will expire quickly.
You should cache the credentials needed to obtain the token (user name and password, app ID and secret, or certificate) and obtain a token when needed.
I've done this for a confidential client application, where I connected to O365 in order to send email.
First, register your app in azure app as the docs say.
Then, set up your confidential client application and use as singleton.
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithRedirectUri(redirectUri)
.WithLegacyCacheCompatibility(false)
.WithAuthority(AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount)
.Build();
app.AddDistributedTokenCache(services => {
services.AddDistributedTokenCaches();
services.AddDistributedSqlServerCache(options => {
options.SchemaName = "dbo";
options.TableName = "O365TokenCache";
options.ConnectionString = sqlCacheConnectionString;
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
services.AddSingleton<IConfidentialClientApplication>(app);
The first time you connect a user, you need to redirect to Microsoft identity. You can create the URL using:
var authUrl = await app.GetAuthorizationRequestUrl(new[] { "email", "offline_access", "https://outlook.office.com/SMTP.Send" }).ExecuteAsync();
(Check your scopes are what you want)
When they come back to your redirect url you then get the code from query string and acquire the refresh token:
var token = await app.AcquireTokenByAuthorizationCode(scopes, code).ExecuteAsync();
When you do this, MSAL will cache the access token and refresh token for you, but here's the thing they don't mention: you have to create the table in SQL yourself! If you don't, it just silently fails.
dotnet tool install -g dotnet-sql-cache
dotnet sql-cache create "<connection string>" dbo O365TokenCache
Once you have the access token the first time you can use the following later
var account = await app.GetAccountAsync(accountId);
var token = await app.AcquireTokenSilent(scopes, account).ExecuteAsync();
When you get the access token the first time, you need to look at token.Account.HomeAccountId.Identifier as this is the ID that you need when you call GetAccountAsync. For some reason, GetAccountsAsync (note the extra "s") always returns empty for me but passing the correct ID to GetAccountAsync does return the right one.
For me, I simply store that ID against the logged in user so that I can get that ID at a later time.
My app first uses the Cognito LOGIN endpoint to obtain an Authorization Code. It then uses the TOKEN endpoint to try and obtain tokens (id_token, access_token, refresh_token) but that fails with unauthorized_client.
I do not understand why, the same client is used to access the LOGIN, and that succeeded in returning an authorization code. I'm following the documentation for the TOKEN endpoint
string clientId = ...
string clientSecret = ...
Uri redirectUri = new Uri("myapp://myhost");
string authorization_code = ... // obtained via HTTP GET on LOGIN endpoint
string accessTokenUrl = "https://<domain>.auth.<region>.amazoncognito.com/oauth2/token";
var queryValues = new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "code", authorization_code },
{ "redirect_uri", redirectUri.AbsoluteUri },
{ "client_id", clientId},
};
using (HttpClient client = new HttpClient())
{
// Authorization Basic header with Base64Encoded (clientId::clientSecret)
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}",
clientId,
clientSecret))));
// Url Encoded Content
var content = new FormUrlEncodedContent(queryValues);
// HTTPS POST
HttpResponseMessage response = await client.PostAsync(accessTokenUrl, content).ConfigureAwait(false);
string text = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
// test = {"error" : "unauthorized_client"}
}
The problem is two-fold:
1- System.Uri.AbsoluteUri adds a trailing / in the returned string so that my redirectUri becomes myapp://myhost/ instead of myapp://myhost
2- AWS Cognito TOKEN endpoint does not accept trailing / in a redirectURI.
The solution:
I now call redirectUri.OriginalUri instead of redirectUri.AbsoluteUri where I build the query to preserve the redirectUri as it was when I built it.
(I don't really have control over this since in my case Xamarin.Auth.OAuthAuthenticator2 calls Uri.AbsoluteUri on my behalf and transforms the redirectUri string I gave it, so I'm going to have to fix Xamarin.Auth).
I am developing an application that needs to send files to Google Cloud Storage.
The webapp will have a HTML page that the user choose files to do upload.
The user do not have Google Account.
The amount files to send is 5 or less.
I do not want to send files to GAE and GAE send to GCS. I would like that my user to do upload directly to GCS.
I did this code for upload:
function sentStorage() {
var file = document.getElementById("myFile").files[0];
url = 'https://www.googleapis.com/upload/storage/v1/b/XXX/o?uploadType=resumable&name=' + file.name;
xhr = new XMLHttpRequest();
var token = 'ya29.XXXXXXXXXXXXXXX';
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', file.type);
// resumable
//url = 'https://www.googleapis.com/upload/storage/v1/b/XXXXXX/o?uploadType=resumable&name=' + file.name;
//xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
//xhr.setRequestHeader('Content-Length', file.size);
xhr.setRequestHeader('x-goog-project-id', 'XXXXXXXXXX');
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.send(file);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
var response = JSON.parse(xhr.responseText);
if (xhr.status === 200) {
alert('codigo 200');
} else {
var message = 'Error: ' + response.error.message;
console.log(message);
alert(message);
}
}
};
}
I get a serviceaccount information (Google Console) and generate a token Bearer for it. I used a python file that read the "json account information" and generate the token.
My requisit is that user do not need to confirm any Google Account information for send files, this obligation is from my application. (Users do not have Google Account) and the html page send the files directly to GCS without send to GAE or GCE, so, I need to use HTML form or Javascript. I prefer Javascript.
Only users of this application can do upload (the application has an authentication with database), so, anonymous user can not do it.
My questions are:
This token will expire? I used a serviceaccount for generate this token.
There is a better api javascript to do it?
This security solution is better or I should use a different approach?
Sending either a refresh or an access token to an untrusted end user is very dangerous. The bearer of an access token has complete authority to act as the associated account (within the scope used to generate it) until the access token expires a few minutes later. You don't want to do that.
There are a few good alternatives. The easiest way is to create exactly the upload request you want, then sign the URL for that request using the private key of a service account. That signed URL, which will be valid for a few minutes, could then be used to upload a single object. You'll need to sign the URL on the server side before giving it to the customer. Here's the documentation on signed URLs: https://cloud.google.com/storage/docs/access-control/signed-urls
Im using version version 1.0.0 of the IdentityServer4 package.
"IdentityServer4": "1.0.0"
I've created a Client
new Client
{
ClientId = "MobleAPP",
ClientName = "Moble App",
ClientUri= "http://localhost:52997/api/",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("SecretForMobleAPP".Sha256())
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api"
},
AllowOfflineAccess = true
}
And the scope/ApiResources
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api", "My API")
};
}
With the following user/TestUser
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password",
Claims = new []
{
new Claim(JwtClaimTypes.Name, "Bob Smith")
}
}
};
}
I'm trying to test the IdentityServer that I have setup from Postman and determine the possible values for the grant_type key value pair.
I can successfully connect when I set the grant_type to client_credentials and wasn't sure if there were other options for the grant_type value.
Working Postman configuration with grant_type set to client_credentials
Short answer
client_credentials is the only grant_type value you can use directly against the token endpoint when using both hybrid and client credentials grant types.
Longer answer
The client credentials grant type is the only one allowing you to hit the token endpoint directly, which is what you did in your Postman example. In that case the authentication is done against the client itself - i.e. the application you registered.
When you use the hybrid grant type, the authentication will be done against the end-user - the user using your application. In that case, you cannot hit the endpoint token directly but you'll have to issue an authorization request to IdentityServer.
When you do so, you won't use the grant_type parameter but the response_type parameter, to instruct IdentityServer what you expect back.
The possible values for response_type when you use the hybrid grant type can be found in IdentityServer constants - they are the last 3 items in the dictionary:
code id_token, which will return an authorization code and an identity token
code token, returning an authorization code and an access token
code id_token token, giving you back an authorization code, an identity token and an access token
After you get the authorization code, you'll be able to exchange it for an access token and possibily a refresh token by hitting the token endpoint.