We have a requirement to generate one time use tokens and use it in password reset emails.
Does Keycloak provide any standard API´s to generate and Validate one time tokens?.
We are using OIDC
Rsource Owner Password Credentials Grant (Direct Access Grants for authentication.
You can implement customized Action Token SPI, and Required Action SPI. Which would generate required token and then challenge user for them.
https://www.keycloak.org/docs/7.0/server_development/index.html#_action_token_spi
Anatomy of Action Token
nonce - Random nonce to guarantee uniqueness of use if the operation can only be executed once (optional). This is a random string that your application must generate.
Here’s an example of Java Servlet code that generates the URL to establish the account link.
KeycloakSecurityContext session = (KeycloakSecurityContext) httpServletRequest.getAttribute(KeycloakSecurityContext.class.getName());
AccessToken token = session.getToken();
String clientId = token.getIssuedFor();
String nonce = UUID.randomUUID().toString();
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
String input = nonce + token.getSessionState() + clientId + provider;
byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
String hash = Base64Url.encode(check);
request.getSession().setAttribute("hash", hash);
String redirectUri = ...;
String accountLinkUrl = KeycloakUriBuilder.fromUri(authServerRootUrl)
.path("/auth/realms/{realm}/broker/{provider}/link")
.queryParam("nonce", nonce)
.queryParam("hash", hash)
.queryParam("client_id", clientId)
.queryParam("redirect_uri", redirectUri).build(realm, provider).toString();
Related
I am using the SignalR .net core client in my project with JWT Tokens.
In the sample code below, the string variable "tokenString" has already been configured as an actual token and therefore i don't need to call upon an external method to create the token, that part has already been done before I reach this method. Using debug, and also testing the "tokenString" value on JWT website, I know the token is working, its just the fact I dont know how to use the ready made token in the SignalR connection method.
How do I configure the SignalR client connection to use this tokenString?
localConConnection = new HubConnectionBuilder()
.WithUrl("https://localhost:44372/LocalConnectorHub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(tokenString); // Not working
// Need a solution like this: options.Token = tokenString
})
.WithAutomaticReconnect()
.Build();
The issue was the fact that the [Authorize] attribute I had configured in the SignalR Hub class needed to define the authentication scheme to use, [Authorize] attribute alone was not enough.
SignalR Hub Class:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class LocalConnectorHub : Hub
{
public async Task SendToMacros(string serverName, string data)
{
await Clients.All.SendAsync("MacrosInbound", serverName, data);
}
public async Task ConnectorStatus(string serverName, string data)
{
await Clients.All.SendAsync("UpdateConnectorStatus", serverName, data);
}
}
SignalR .NET Core Client Connection:
localConConnection = new HubConnectionBuilder()
.WithUrl("https://localhost:44372/LocalConnectorHub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(tokenString);
})
.WithAutomaticReconnect()
.Build();
await localConConnection.StartAsync();
Further example code from the startup.cs class (inside configure services method), this is posted to help one of our fellow members in the comments below:
// Retrieve the secret key from the appsettings.json file used for encryption
// when generating the JWT token for REST API authentication.
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value);
// Added to original .net core template.
// The preceding code configures multiple authentication methods. The app uses cookie-based authentication to log in
// via the browser using the identity manager. The second methid uses JWT bearer authentication for the REST API.
// The preceding cookie configuration configures Identity with default option values.
// Services are made available to the app through dependency injection.
// Cookie configuration MUST be called after calling AddIdentity or AddDefaultIdentity.
// IMPORTANT NOTE:
// When we decorate controllers or classes with use the [Authorize] attribute, it actually binds to the first authentication
// system by default (in this case cookie authentication) The trick is to change the attribute to specify which authorization
// service we want to use. Anexample for a protected respurce for a REST API controller would be to decorate using:
// "[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]"
services.AddAuthentication()
.AddCookie(options =>
{
// Cookie settings
options.Cookie.Name = "MyCompanyName";
// HttpOnly is a flag that can be used when setting a cookie to block access to the cookie from client side scripts.
// Javascript for example cannot read a cookie that has HttpOnly set. This helps mitigate a large part of XSS attacks
// as many of these attempt to read cookies and send them back to the attacker, possibly leaking sensitive information
// or worst case scenario, allowing the attacker to impersonate the user with login cookies.
options.Cookie.HttpOnly = true;
// CookieAuthenticationOptions.ExpireTimespan is the option that allows you to set how long the issued cookie is valid for.
// The cookie is valid for (XX) minutes from the time of creation. Once those XX minutes are up the user will have to sign
// back in becuase if the SlidingExpiration is set to false.
// If SlidingExpiration is set to true then the cookie would be re-issued on any request half way through the ExpireTimeSpan.
// For example, if the user logged in and then made a second request half way through the permitted timespan then the cookie
// would be re-issued for another (XX) minutes. If the user logged in and then made a second request AFTER (XX) minutes later
// then the user would be prompted to log in.
// You can also change the units i.e. TimeSpan.FromHours(10); OR TimeSpan.FromDays(10);
// In a nutshell, setting the options.ExpireTimeSpan is equivalent to setting an idle time out period...
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
// Sliding expiration resets the expiration time for a valid authentication cookie if a request is made and more than half of the
// timeout interval has elapsed.If the cookie expires, the user must re - authenticate.Setting the SlidingExpiration property to
// false can improve the security of an application by limiting the time for which an authentication cookie is valid, based on the
// configured timeout value.
options.SlidingExpiration = true;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
// The "iss" (issuer) claim identifies the principal that issued the JWT. The processing of this
// claim is generally application specific. The "iss" value is a case-sensitive string containing
// a StringOrURI value. Use of this claim is OPTIONAL.
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
// The "iss" (issuer) claim identifies the principal that issued the JWT.The processing of this
// claim is generally application specific. The "iss" value is a case-sensitive string containing
// a StringOrURI value.Use of this claim is OPTIONAL.
ValidateIssuer = false,
// Usually, this is your application base URL
ValidIssuer = "http://localhost:45092/",
// The "aud" (audience) claim identifies the recipients that the JWT is intended for. Each principal
// intended to process the JWT MUST identify itself with a value in the audience claim. If the principal
// processing the claim does not identify itself with a value in the "aud" claim when this claim is present,
// then the JWT MUST be rejected. In the general case, the "aud" value is an array of case-sensitive strings,
// each containing a StringOrURI value. In the special case when the JWT has one audience, the "aud" value
// MAY be a single case-sensitive string containing a StringOrURI value. The interpretation of audience
// values is generally application specific. Use of this claim is OPTIONAL.
ValidateAudience = false,
//Here, we are creating and using JWT within the same application.
//In this case, base URL is fine.
//If the JWT is created using a web service, then this would be the consumer URL.
ValidAudience = "http://localhost:45092/",
// The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted
// for processing. The processing of the "exp" claim requires that the current date/time MUST be before the
// expiration date/time listed in the "exp" claim.
RequireExpirationTime = true,
// Check if token is not expired and the signing key of the issuer is valid (ValidateLifetime = true)
ValidateLifetime = true,
};
// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or
// Server-Sent Events request comes in.
// Sending the access token in the query string is required due to
// a limitation in Browser APIs. We restrict it to only calls to the
// SignalR hub in this code.
// See https://learn.microsoft.com/aspnet/core/signalr/security#access-token-logging
// for more information about security considerations when using
// the query string to transmit the access token.
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
Appsettings.json file (dont store keys here for production :)
"AppSettings": {
"Token": "secret key for jwt"
}
I have a realm with several OpenId clients using SSO. I need to determine from which client each Keycloak user came from.
How can I get this information?
Answering my own question.
I didn't find natively this information in Keycloak's token. So I added a script.
In Authentication > Registration flow, I add an execution of type "Script" with the following function:
function authenticate(context) {
var username = user ? user.username : "anonymous";
var uri = context.getUriInfo();
LOG.info("setClientIdAttribute for URI " + context.getUriInfo().getRequestUri());
if (uri !== null) {
var clientId = uri.getQueryParameters().getFirst("client_id");
if (clientId !== null) {
LOG.info("Attribute 'origin' set with value " + clientId + " for user " + username);
user.setSingleAttribute('origin', clientId);
}
}
context.success();
}
If decoded each JWT has parameter azp, which is the client id.
If you decode the token, the field "aud" is your "client_id".
Check this information using jwt.io.
I use Google ID tokens to sign in users to my webservice. As part of validating the token it receives from Google, the webservice checks that email_verified = true in the token's payload.
Some of my users signed up for a Google-account with their non-Gmail, non-Google Apps email address. They did click the link in the email that Google sent them after sign-up, to verify their email address.
When those users try to login to my webservice, I get email_verified = false in the token's payload.
What does this mean and can/ should I ignore this in validating the token?
There are a couple of different ways in which you can validate the integrity of the ID token on the server side:
"Manually" - constantly download Google's public keys, verify signature and then each and every field, including the iss one; the main advantage (albeit a small one in my opinion) I see here is that you can minimize the number of requests sent to Google.
"Automatically" - do a GET on Google's endpoint to verify this token
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}
Using a Google API Client Library - like the official one.
Here's how this could look:
private const string GoogleApiTokenInfoUrl = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}";
public ProviderUserDetails GetUserDetails(string providerToken)
{
var httpClient = new MonitoredHttpClient();
var requestUri = new Uri(string.Format(GoogleApiTokenInfoUrl, providerToken));
HttpResponseMessage httpResponseMessage;
try
{
httpResponseMessage = httpClient.GetAsync(requestUri).Result;
}
catch (Exception ex)
{
return null;
}
if (httpResponseMessage.StatusCode != HttpStatusCode.OK)
{
return null;
}
var response = httpResponseMessage.Content.ReadAsStringAsync().Result;
var googleApiTokenInfo = JsonConvert.DeserializeObject<GoogleApiTokenInfo>(response);
if (!SupportedClientsIds.Contains(googleApiTokenInfo.aud))
{
Log.WarnFormat("Google API Token Info aud field ({0}) not containing the required client id", googleApiTokenInfo.aud);
return null;
}
return new ProviderUserDetails
{
Email = googleApiTokenInfo.email,
FirstName = googleApiTokenInfo.given_name,
LastName = googleApiTokenInfo.family_name,
Locale = googleApiTokenInfo.locale,
Name = googleApiTokenInfo.name,
ProviderUserId = googleApiTokenInfo.sub
};
}
I'm trying to change Box folder collaboration type for user from salesforce Apex trigger. The first thoughts were to use box.Toolkit but it looks like this class does not have updateCollaboration or changeCollaboration method, only create. I guess my only option is to use Box's Rest API. Is there any way I can get service account token in Apex so I can use it in a callout?
I have created a special "Tokens" object in Salesforce with two fields: access token and refresh token. I then have a batch job that runs to update the access token every 55 minutes such that they never expired.
Here is a code snippet in APEX using the Tokens object.
#future(callout=true)
public static void updateTokens(){
//app info for authenticating
String clientID = 'MY_CLIENT_ID';
String clientSecret = 'MY_CLIENT_SECRET';
//look up value of existing refresh token
Token__c myToken = [SELECT Name, Value__c FROM Token__c WHERE Name='Refresh'];
Token__c myAccessToken = [SELECT Name, Value__c FROM Token__c WHERE Name='Access'];
String refreshToken = myToken.Value__c;
String accessToken = myAccessToken.Value__c;
//variables for storing data
String BoxJSON = '';
String debugTxt = '';
//callout to Box API to get new tokens
HttpRequest reqRefresh = new HttpRequest();
reqRefresh.setMethod('POST');
String endpointRefresh = 'https://www.box.com/api/oauth2/token';
reqRefresh.setEndpoint(endpointRefresh);
String requestBody = ('grant_type=refresh_token&refresh_token=' + refreshToken + '&client_id=' + clientID + '&client_secret=' + clientSecret);
reqRefresh.setBody(requestBody);
System.debug('Body of refresh request: ' + requestBody);
//Create Http, send request
Http httpRefresh = new Http();
Boolean successRefresh = false;
while (successRefresh == false){
try{
HTTPResponse resRefresh = httpRefresh.send(reqRefresh);
BoxJSON = resRefresh.getBody();
System.debug('Body of refresh response: ' + BoxJSON);
successRefresh = true;
}
catch (System.Exception e){
System.debug('Error refreshing: ' + string.valueof(e));
if (Test.isRunningTest()){
successRefresh = true;
}
}
}
Keep in mind that if you are using the Box for Salesforce integration your administrator can set the option for the permissions on the folders to sync with Salesforce permissions. This would reverse any changes you make to collaborations. Check out more about Box's Salesforce integration permissions here: https://support.box.com/hc/en-us/articles/202509066-Box-for-Salesforce-Administration#BfS_admin_perm
We are implementing STS (claim based authentication) for the the REST based services. One of the reasons amongst many when we decide to create REST services (with JSON) was the small footprint over the wire. With STS, the SAML token with just a few claims the SAML size becomes few K bytes. For most of the REST calls where we are not returning list of objects, the response size is low 100s bytes and for those calls this token seems too much of overhead. How do you dealt with this in your projects?
... Or JWT (JSon Web Token). ACS supports these too.
Check this article: JSON Web Token Handler for the Microsoft .NET Framework 4.5
Here is a usage example of this library with .Net 4.5 that issues and validates a JWT signed with symmetric key based HMAC SHA256.
string jwtIssuer = "MyIssuer";
string jwtAudience = "MyAudience";
// Generate symmetric key for HMAC-SHA256 signature
RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider();
byte[] keyForHmacSha256 = new byte[64];
cryptoProvider.GetNonZeroBytes(keyForHmacSha256);
///////////////////////////////////////////////////////////////////
// Create signing credentials for the signed JWT.
// This object is used to cryptographically sign the JWT by the issuer.
SigningCredentials sc = new SigningCredentials(
new InMemorySymmetricSecurityKey(keyForHmacSha256),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
///////////////////////////////////////////////////////////////////
// Create token validation parameters for the signed JWT
// This object will be used to verify the cryptographic signature of the received JWT
TokenValidationParameters validationParams =
new TokenValidationParameters()
{
AllowedAudience = s_jwtAudience,
ValidIssuer = s_jwtIssuer,
ValidateExpiration = true,
ValidateNotBefore = true,
ValidateIssuer = true,
ValidateSignature = true,
SigningToken = new BinarySecretSecurityToken(keyForHmacSha256),
};
///////////////////////////////////////////////////////////////////
// Create JWT handler
// This object is used to write/sign/decode/validate JWTs
JWTSecurityTokenHandler jwtHandler = new JWTSecurityTokenHandler();
// Create a simple JWT claim set
IList<Claim> payloadClaims = new List<Claim>() { new Claim("clm1", "clm1 value"), };
// Create a JWT with signing credentials and lifetime of 12 hours
JWTSecurityToken jwt =
new JWTSecurityToken(jwtIssuer, jwtAudience, payloadClaims, sc, DateTime.UtcNow, DateTime.UtcNow.AddHours(12.0));
// Serialize the JWT
// This is how our JWT looks on the wire: <Base64UrlEncoded header>.<Base64UrlEncoded body>.<signature>
string jwtOnTheWire = jwtHandler.WriteToken(jwt);
// Validate the token signature (we provide the shared symmetric key in `validationParams`)
// This will throw if the signature does not validate
jwtHandler.ValidateToken(jwtOnTheWire, validationParams);
// Parse JWT from the Base64UrlEncoded wire form (<Base64UrlEncoded header>.<Base64UrlEncoded body>.<signature>)
JWTSecurityToken parsedJwt = jwtHandler.ReadToken(jwtOnTheWire) as JWTSecurityToken;
You can use SAML tokens with REST endpoints, but more often you will find people using Simple Web Tokens (SWT) instead. Smaller, simpler, etc.
ACS (Access Control Service in Windows Azure PLatform) implements this, for example.