Vertx SockJs Eventbus Authentication - websocket

I'm trying to make a sock.js connection from the frontend to the vertx backend.
my initial try looked like this:
let token = '<the token>';
let data = {'Authorization' : 'Bearer ' + token};
let eb = new EventBus("http://localhost:8080/eventbus");
eb.onopen = function () {
eb.registerHandler('notifications', data, (err, msg) => {
// handle the response
});
}
this doesn't work since I need to send the auth data on EventBus creation, even though the official sock.js documentation states that this is not supported. Obviously now sending new EventBus("http://localhost:9090/eventbus", data) doesn't work either.
https://github.com/sockjs/sockjs-node#authorisation
my backend handler for this:
final BridgeOptions bridgeOptions = new BridgeOptions()
.addOutboundPermitted(new PermittedOptions().setAddress("notifications"))
final SockJSHandler sockJSHandler = SockJSHandler.create(vertx).bridge(bridgeOptions, event -> {
event.complete(true);
});
router.route("/eventbus/*").handler(ctx -> {
String token = ctx.request().getHeader("Authorization"); // null
});
router.route("/eventbus/*").handler(sockJSHandler);
whatever I tried the header field Authroization is always null.
What is the standard way to authenticate the sock.js connection and register to an eventbus request in vertx?

SockJS uses WebSockets by default. You can't add custom headers (Authorization, etc) using JavaScript WebSocket API. Read this thread for more explanation.
I see 2 ways, how you can add authorization:
Just add token parameter to URL:
let eb = new EventBus("http://localhost:8080/eventbus?token=" + token);
and here's how you can get it on a server:
String token = ctx.request().getParam("token");
Send authorization message after connecting to the server. It can be some JSON object, which contains token field.
I think, 1st option is enough, however, 2nd one can be harder to implement in terms of Event Bus and SockJS.

Since sending Authorization header is not possible, attaching a token query parameter (as described by #berserkk) is the way to go.
However, in some circumstances, it may be undesirable to send your main login token in plain text as a query parameter because it is more opaque than using a header and will end up being logged whoknowswhere. If this raises security concerns for you, an alternative is to use a secondary JWT token just for the web socket stuff.
Create a REST endpoint for generating this JWT, which can of course only be accessed by users authenticated with your primary login token (transmitted via header). The web socket JWT can be configured differently than your login token, e.g. with a shorter timeout, so it's safer to send around as query param of your upgrade request.
Create a separate JwtAuthHandler for the same route you register the SockJS eventbusHandler on. Make sure your auth handler is registered first, so you can check the web socket token against your database (the JWT should be somehow linked to your user in the backend).

I think best way to secure a web-socket is using CORS check
Cross Origin Resource Sharing is a safe mechanism for allowing resources to be requested
router.route().handler(CorsHandler.create(your host origin path).allowCredentials(true));
We can add more layer of security also using sockjs :
Allow events for the designated addresses in/out of the event bus bridge
BridgeOptions opts = new BridgeOptions()
.addInboundPermitted(new PermittedOptions().setAddressRegex(Constants.INBOUND_REGEXP));

Related

Retrieving a trusted client token within a Keycloak addon (SPI)

As part of our effort to be GDPR compliant, I’m trying to extend our Keycloak server with some custom functionality, to be triggered when a user is removed:
sending a confirmation message to a Slack channel
sending a HTTP request to another service we built, to delete any assets that user might have created there
The first one was easy. It's implemented as a SPI event listener, following this example:
#Override
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
keycloakSessionFactory.register(
(event) -> {
// the user is available via event.getUser()
if (event instanceof UserModel.UserRemovedEvent){
userRemovedMessageHandler.handleUserRemoveEvent(session, (UserRemovedEvent) event);
LOG.debug("User removed event happened for user: " + ((UserRemovedEvent) event).getUser().getId());
}
});
}
But I'm stuck on the second task (sending the HTTP request). The other service is set up to require a trusted client token, using a designated Keycloak Client and scope. The equivalent POST request to auth/realms/{realm}/protocol/openid-connect/token is done with these parameters:
grant_type: password
username: {user that is about to be deleted}
password: {user password}
client_id: {the specific client}
client_secret: {that client's secret}
scope: usersets
I can’t find out how to do that from within the SPI code. I can retrieve a regular Access Token (as described in this other SO post):
private String getAccessToken(KeycloakSession session, UserModel deleteUser) {
KeycloakContext keycloakContext = session.getContext();
AccessToken token = new AccessToken();
token.subject(deleteUser.getId());
token.issuer(Urls.realmIssuer(keycloakContext.getUri().getBaseUri(), keycloakContext.getRealm().getName()));
token.issuedNow();
token.expiration((int) (token.getIat() + 60L)); //Lifetime of 60 seconds
KeyWrapper key = session.keys().getActiveKey(keycloakContext.getRealm(), KeyUse.SIG, "RS256");
return new JWSBuilder().kid(key.getKid()).type("JWT").jsonContent(token).sign(
new AsymmetricSignatureSignerContext(key));
}
but that's not the token I need. In order to retrieve the required trusted client token I’d need the internal Keycloak / SPI-equivalent of the above POST request, but I can’t figure out how to do that or where to look.
Besides, I'm not sure if it is even possible to retrieve a trusted client token for a user that is in the process of being deleted. In other words, I don’t know if the UserRemovedEvent is fired before, during or after the user is actually deleted, or if Keycloak waits until any custom EventHandlers have finished running.
(In order to figure that out, I'll try if it is possible to retrieve that token using a regular http POST request from within the add-on code, but I have no idea if it's possible to connect to Keycloak like that from inside. I'll update the question if that works.)
I'd greatly appreciate any suggestion!
(I also asked this in the Keycloak discourse group here but it looks as if it will remain unanswered there)

How to log in from a single page react app to Laravel 7.x on another domain?

I have a single page create-react-app running on localhost:3000 and I want to log in to a laravel 7.x instance running on myapp.loc (vhost).
Eventually I would like a single page running on app.mysite.com with laravel running on api.mysite.com.
I'm able to log in to my laravel instance directly from myapp.loc. I've installed Laravel passport and the scaffolding, etc and can create Client IDs and Secrets, but I'm unsure if they are needed and if so, how to use them.
What I am unsure of and cannot find any documentation for, is how to log in to laraval from my SPA (running on localhost:3000). I have set my CORS headers and can connect requests that don't require auth, but I don't know how to log in or structure auth requests once logged in.
I can't find any documentation on axios.post / get requests with a focus on logging in from another domain and maintain user-based access.
Since I don't know enough to ask a concise question, there are three layers that I feel like I should be searching for an answer.
Is it possible for laravel to act as the backend for my single page app from another domain?
If so, are there documented, best practices / well worn paths for accomplishing this?
If so, what would a sample axios login and subsequent session call look like? (e.g. payload and header shape)
Yes you can, I suggest to use https://laravel.com/docs/7.x/sanctum instead of passport because is easier to setup and it was created especially for this scenario.
You need to configure CORS using the official Laravel Package https://github.com/fruitcake/laravel-cors this way you will open Laravel's CORS to be able to reach it from anywhere localhost, or any domain that you can set up into allowed_origins. inside the cors.php config file according to the documentation of the package.
After configuring Sanctum/Passport and ensuring you are generating the required token for your SPA using the createToken method described in Sanctum or Passport docs, you have to save the token to connect to your protected endpoints however they recommend to use cookie SPA based authentication but that's not strictly necessary.
Create an API Service
In this example I will create an API Service to encapsulate API calls
import axios from 'axios';
const URI = 'https://yourlaravel.api/api/';
axios.defaults.headers.common = { Accept: 'application/json', 'Content-Type': 'application/json' };
const ApiInstance = axios.create();
const API = {
login: (user) => {
return ApiInstance.post(`${URI}login`, user);
},
getUser: () => {
return ApiInstance.get(`${URI}user`);
},
setUser: (user) => {
return ApiInstance.post(`${URI}user`, user);
},
};
Send A Login Request to your login endpoint and save the token
import API;
API.login({email:'mail#domain.com',password:'32323'})
.then(response=>{
//save the token
//response.data.accessToken
})
Fetch data from your protected endpoints using the token
//Set the default authorization header when you have an access token
axios.defaults.headers.common = {'Authorization': `Bearer ${token}`}
//Get your data
API.getUser().then(response=>{
//response.data
})
//Post something
API.setUser({user:'Os'}).then(response=>{
//response.data
})
All those things are possible, you just need to set up cors and you are good to go. For auth you can use passport or your own custom app key setup, it all depends on what you are trying to achieve. I suggest reading up about RESTfull apis, that would be a good start.
In order to perform a handshake between FE and BE on FE you would have a login form submission of which will send e request to BE (backend api) and if login is success you send back a key which then FE should store. Any future requests from FE should append that key in the header to gain access to authorised areas.
There should be plenty of information on this subject (RESTfull Api & Token authentication) on google.

How to force an oAuth token renewal (access-token + refresh token) with Spring boot keycloak adapter + spring security.?

I have a multi-tenant application (springboot keycloak adapter + spring security) secured by Keycloak. Given the multi-tenant nature of the project, I wrote a multi-client connector which works fine.
On the official Keycloak doc, it is recommended (for multi-tenant applications) to model each tenant as a new realm, but for me it works better to have multiple clients within the same same realm. This is due to following advantages:
Client scopes, groups and other configs can be shared
Users don't need to be duplicated on N different realms
SSO login works perfectly within same realm clients (by using bearer
services +CORS)
So, everything works fine except for 1 thing, my initial SSO access_token (which is then shared across all bearer-only services by means of CORS) is kind of big (it shows all the resources - tenants - and its roles within each resource/tenant).
I'd like to limit the size of the access_token, by means of using "scopes" to restrict the roles in the token to only those meaningful to the tenant where I'm logged in at that time. For this, I'm manually firing a Request to the auth server (outside of the standard functionality provided by springboot/spring security) with the goal of manually overwriting whatever access-token exists within my app, with the new one generated by my extra request.
My "new" token request looks similar to this:
SimpleKeycloakAccount currentUserAccount = (SimpleKeycloakAccount) auth.getDetails();
String authServerUrl = currentUserAccount.getKeycloakSecurityContext().getDeployment().getAuthServerBaseUrl();
String realm = currentUserAccount.getKeycloakSecurityContext().getDeployment().getRealm();
String resource = currentUserAccount.getKeycloakSecurityContext().getDeployment().getResourceName();
String refreshToken = currentUserAccount.getKeycloakSecurityContext().getRefreshToken();
String token = currentUserAccount.getKeycloakSecurityContext().getTokenString();
Http http = new Http( new Configuration(authServerUrl, realm, resource,
currentUserAccount.getKeycloakSecurityContext().getDeployment().getResourceCredentials()
, null),
(params, headers) -> {});
String url = authServerUrl + "/realms/" + realm + "/protocol/openid-connect/token";
AccessTokenResponse response = http.<AccessTokenResponse>post(url)
.authentication()
.client()
.form()
.param("grant_type", "refresh_token")
.param("refresh_token", refreshToken)
.param("client_id", resource)
.param("client_secret", "SOME_SECRET")
.param("scope", "SOME_SCOPE_TO_RESTRICT_ROLES")
.response()
.json(AccessTokenResponse.class)
.execute();
// :) - response.getToken() and response.getRefreshToken(), contain new successfully generated tokens
My question is, how can I force my-app to change/reset the standard access-token & refresh_token obtained by the usual means, with these "custom created" tokens? or is that possible at all?
Thx for any feedback!
Further Information
To clarify more, lets analyze the behavior of a typical springboot/spring security project integrated with Keycloak:
You protect your endpoints with "roles" via configurations (either on the application.properties, or on the SecurityContext)
You know that this Spring application talks in the back channel with the Keycloak authorization server, that's how you become the access_token (But all this is a black box for the developer, you only know a Principal was created, a Security Context, Credentials; etc - everything happens behind the curtains)
Considering those 2 points above, imagine that you use an Http library to basically request a new token towards the auth server token endpoint like in the code above (yes filtered by scopes and everything). So the situation now is that though you have created a valid access_token (and refresh_token); since they were created "manually" by firing a request towards the token endpoint, this new token hasn't been "incorporated" to the application because No new Principal has been created, no new security context has been generated, etc. In other words, to the springboot application this new token is non-existent.
What I'm trying to accomplish is to tell sprinboot/spring security: "Hey pal, I know you didn't generate this token yourself, but please accept it and behave as if you'd have created it".
I hope this clarifies the intent of my question.
You can revoke a token using org.springframework.security.oauth2.provider.token.ConsumerTokenServices#revokeToken method.
On the Autorization Server:
#Resource(name="tokenServices")
ConsumerTokenServices tokenServices;
#RequestMapping(method = RequestMethod.POST, value = "/tokens/revoke/{tokenId:.*}")
#ResponseBody
public String revokeToken(#PathVariable String tokenId) {
tokenServices.revokeToken(tokenId);
return tokenId;
}
Of course, you'll have to secure this method since is a very sensitive one.
In the case that each tenant is a separate client you can just use keycloak's "Scope" mapping at each client. Just turn off Full Scope Allowed and your tokens will only contain the user's roles for that specific client (tenant).
"Scope Mappings" is a a non intuitive way of saying "Define what roles should go into the access token" :-)
When turned off the UI changes and you even can configure what other roles of other clients should additionally go into the access token.
Just to give some closure to this question:
No, there doesn't seem to be any elegant or intended way to force a manual token renewal by means of using springboot/spring security keycloak connector.
The Javascript connector can do this trivially like this:
// for creating your keycloak connector
var keycloak = Keycloak({
url: 'http://localhost:8080/auth',
realm: '[YOUR_REALM]',
clientId: '[YOUR_CLIENT]'
});
// for login in (change scopes list to change access capabilities)
var options = {
scope: [EMPTY_STRING_SEPARATED_LIST_OF_SCOPES] // <-- here specify valid scopes
};
keycloak.login(options); // <-- receive a new token with correctly processed scopes
Given how easy it is to do this with the Keycloak client JS adapter, and how obscure it is to do this with the springboot/spring security adapter, it follows following:
Security design seems intended to have 2 (Keycloak security) layers; the first is a front-facing public client (usually password protected), and the 2nd layer is composed of several bearer-only services which would ussually only accept acces-tokens. If for those bearer-only services you want to implement finner grained control via scopes, you achieve that trivially by using a javascript based Keycloak client (other connectors as explained won't deal nicely with the header modification necessary to deal with OAuth2 scopes).

Identityserver3: Token validation requests not visible in Fiddler

In fiddler composer, I execute a call to a local api that is secured by a local identityserver3.
I added the token, and I configured the middleware to validate the token on the server. Tt works fine, in Idenityserver3 log file I see a succesful token validation logmessage for the configured scope.
I assume that, with htis config, each time the api is called, the Idsrv3 middleware calls the token validation endpoint under the hood.
My issue is that fiddler does not show this middleware request, just the call to the api itself. Is this due to fiddler settings or is there another reason this request is invisible to fiddler?
Is there a way to display it?
Thanks
It could be that .NET is not routing the localhost traffic to fiddler proxy. For workaround check this.
http://docs.telerik.com/fiddler/observe-traffic/troubleshooting/notraffictolocalhost
Also, check if you have configured validation mode properly. According to documentation https://identityserver.github.io/Documentation/docsv2/endpoints/identityTokenValidation.html validation endpoint is useful if you have no access to crypto libraries locally.
Valid validation modes are
ValidationMode.Both - Use local validation for JWTs and the validation endpoint for reference tokens
ValidationMode.Local - Use local validation oly (only suitable for JWT tokens)
ValidationMode.ValidationEndpoint - Use the validation endpoint only (works for both JWT and reference tokens)
You can set the validation mode in IdentityServerAuthenticationOptions
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44333/core",
//RequiredScopes = new[] { "write" },
// client credentials for the introspection endpoint
ClientId = "write",
ClientSecret = "secret",
ValidationMode = ValidationMode.Local
});

REST API Login approach

We are building system that required login information for all pages. the application is designed to be Restful application using codeigniter as Phil Sturgeon library. This library is using API Key only to authorize api calls via sending it with every request over HTTPS connection.
Even if it using 2 way authentication or only API Key. What i am searching for a while is the following scenario:
User request the application for the first time (ex: https://www.xyz.com) then it will be redirected to the login page to check credentials
User enter the usernam/password and sent it via POST over the https
Server check if the information is valid then:
API KEY should be provided by the server to the client as a resource identified by this username (Here is the question???!!!)
How to send the API Key to the client in a secure way?
1) Could i use session-cookies and restore the API KEY in a cookie then use this API KEY on every coming request (This is violent the Stateless of the Rest and i don't sure if it securely enough).
2) Actually i don't know other options :) it's your turn if you could help
If you could give an example it would be a great help as i found and read lots of articles
:)
Since the connection is HTTPS, anything you send over the wire is secure (theoretically and provided you aren't being mitm'd). Not sure if the whole API is served over HTTPS (you didn't specify), so even though you could return the key as part of the login (while still under the umbrella of HTTPS), if the rest of the api isn't HTTPS, the key could be sniffed on the next request.
Sessions and cookies aren't typically part of a RESTful application; REST is stateless.
Something like a revolving key would be better for non-HTTPS (would work with HTTPS too). You login via HTTPS, server returns the api key, you use it on the next request, server returns new api key, you use it on the next request and so on. While it's better than a single api key over non-HTTPS, it's not perfect. If someone sniffs the response from one of the subsequent requests and you don't end up consuming that key, they can use it. This shrinks the attack vector to a non-HTTPS response from server to client since if a request from client to server is sniffed, the api key will have already been consumed by your legitimate request. However, more should be done to secure the api if you aren't serving it over HTTPS.
If it were me, I'd look into request signing + https. There's some talk of request signing here: https://stackoverflow.com/a/8567909/183254
There's also some info on digest auth at the Securing the API section of http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/
A pseudo-code example js function on the client
function get_calendar(){
var key = $('#api_key').value();
$.ajax({
type: 'get',
url: '/index.php/api/calendar?key=' + key,
success: function(response){
// show calendar
// ...
// set received api key in hidden field with id api_key
$('#api_key').value(response.api_key)
}
})
}
Example controller method:
function calendar_get($api_key = ''){
if($api_key_matches){//verify incoming api key
$r = array();
$r['calendar'] = $this->some_model->get_calendar();
$r['api_key'] = $this->_generate_api_key();// generate or get api key
}
$this->response($r);
}

Resources