Is there a way to prevent users from editing the local storage session? - ember-simple-auth

I am creating a relational blog where I make use of ember_simple_auth:session to store the session like
{"authenticated":{"authenticator":"authenticator:devise","token":"rh2f9iy7EjJXESAM5koQ","email":"user#example.com","userId":1}}
However, on the developer tools on Chrome (and possibly on other browsers), it is quite easy to edit the email and userId in order to impersonate another user upon page reload.
EDIT #1
From the conversation with Joachim and Nikolaj, I now realized that the best way to tackle this problem is to probe the localStorage authenticity every time I need it (which is only on page reload) instead of attempting to prevent edits.
In order to validate authenticity, I create a promise that must be solved before the AccountSession can be used. The promise serverValidation() requests to create a token model with the current localStorage info, and when the server gets it, it validates the info and responds 200 with a simple user serialization with type as token if the information is legit. You can check more info on the Source Code.
Session Account
import Ember from 'ember';
const { inject: { service }, RSVP } = Ember;
export default Ember.Service.extend ({
session: service('session'),
store: service(),
serverValidation: false,
// Create a Promise to handle a server request that validates the current LocalStorage
// If valid, then set SessionAccount User.
loadCurrentUser() {
if (!Ember.isEmpty(this.get('session.data.authenticated.userId'))) {
this.serverValidation().then(() => {
return new RSVP.Promise((resolve, reject) => {
const userId = this.get('session.data.authenticated.userId');
// Get User to Session-Account Block
if(this.get('serverValidation') === true) {
return this.get('store').find('user', userId).then((user) => {
this.set('user', user);
resolve();
}).catch((reason) => {
console.log(reason.errors);
var possible404 = reason.errors.filterBy('status','404');
var possible500 = reason.errors.filterBy('status','500');
if(possible404.length !== 0) {
alert('404 | Sign In Not Found Error');
this.get('session').invalidate();
}
else if(possible500.length !== 0) {
alert('500 | Sign In Server Error');
this.get('session').invalidate();
}
reject();
});
}
else{
alert('Session for Server Validation failed! Logging out!');
this.get('session').invalidate();
resolve();
}
});
});
} else {
// Session is empty...
}
},
serverValidation() {
return new RSVP.Promise((resolve) => {
var tokenAuthentication = this.get('store').createRecord('token', {
id: this.get('session.data.authenticated.userId'),
email: this.get('session.data.authenticated.email'),
authenticity_token: this.get('session.data.authenticated.token'),
});
tokenAuthentication.save().then(() => {
this.set('serverValidation',true);
console.log('Server Validation complete with 200');
resolve();
}).catch((reason) => {
this.set('serverValidation',false);
resolve();
});
});
}
});
Token Controller
# Users Controller: JSON response through Active Model Serializers
class Api::V1::TokensController < ApiController
respond_to :json
def create
if token_by_id == token_by_token
if token_by_email == token_by_id
render json: token_by_id, serializer: TokenSerializer, status: 200
else
render json: {}, status: 404
end
else
render json: {}, status: 404
end
end
private
def token_by_id
User.find(user_params[:id])
end
def token_by_email
User.find_by(email: user_params[:email])
end
def token_by_token
User.find_by(authentication_token: user_params[:authenticity_token])
end
def user_params
ActiveModelSerializers::Deserialization.jsonapi_parse!(params.to_unsafe_h)
end
end

There is no way to prevent a user from editing the content of his local storage, session storage, or cookies.
But this should not worry you. The user is identified through the value of the token. The token is generated and sent to him by the authenticator when he logs in. To impersonate another user by editing the session data he would have to know that the other user is logged in, and know the token of that user.

Token is already signed on the server side, a standard JWT mechanism.
Having said that, there can be a couple of ways to check tempering in local storage:
Generate a token the way you already do.
Generate a random secret key to be kept on the server.
Generate a corresponding HMAC using this secret key.
Send the token + HMAC to the user.
When the user sends you this token, first check if HMAC is correct, if not then reject the token right away.
If HMAC is correct, validate the token the way you already do.
Another way:
Along with the token, a HMAC checksum too can be stored separately, and when sent back to the server by the client, check if checksum matches.

Related

Getting a Azure AD refresh token and auth code using MSAL

I am working on an application with Ruby on Rails back-end and EmberJS Front-end.
I would like to achieve the following.
Log in user with MSAL with Ember FE
Get the auth_code and pass on to the back end
From the back end, fetch access token and refresh token.
Use it to send email using azure Graph API.
Basically I would like to perform step 1 of azure ad auth, i.e. fetching the auth_code, at the front end. And then perform step 2 at the back end. And then refresh the tokens at the back-end as needed.
I believe MSAL provides no good way to achieve this. When user consents (Step 1), it uses the auth_code by itself to fetch access_token and refresh_token (Step 2), and cashes it without exposing the values. It invokes acquireTokenSilent method to refresh the tokens when needed.
I can see that MSAL also has a ssoSilent method, which performs only step 1 of auth and returns auth code. I tried to use it in the following manner
signIn = () => {
myMSALObj
.loginPopup(loginRequest)
.then(this.handleResponse)
.catch((error) => {
console.error(error);
});
};
handleResponse = (resp) => {
console.log('>>>> Response', resp);
// this.token = resp;
if (resp !== null) {
username = resp.account.username;
// showWelcomeMessage(resp.account);
resp.account = myMSALObj.getAccountByUsername(username);
console.log('Resp => ', resp);
myMSALObj
.ssoSilent(resp)
.then((r) => {
console.log(r);
})
.catch((e) => {
console.log('Error ->', e);
});
} else {
// loadPage();
}
};
This always ends up in the following error
InteractionRequiredAuthError: interaction_required: Silent authentication was denied. The user must first sign in and if needed grant the client application access to the scope ...
Even when the user has just consented for these scopes.
Is there something I am missing?
or
Is there any better way to achieve this functionality?
Thanks in advance for any help
I am using Rails 6 with Ember 4.1

How do I manage access token in Nodejs application?

Click here to see Overview Diagram
Hi All,
I have service A that needs to call service B in different network domain. To make a call to service B, service A gets access token from identity provider then call service B with the access token in Http Authorization header. When there are multiple or concurrent requests to service A, I want to minimize the calls to identity provider to get access token. So I plan to implement caching by using https://www.npmjs.com/package/lru-cache which is similar to the approach using by google-auth-library
https://github.com/googleapis/google-auth-library-nodejs/blob/master/src/auth/jwtaccess.ts.
The service A will call identity provider to get access token and store to the cache. When the next request come in, the service A will use the token from cache and calls service B. If the cache item is expired, then service A will get service token and store in cache.
I have the following questions:
How do we handle race condition when there are concurrent request to service A that can cause multiple requests are sent to get access token and have multiple updates to the cache?
Let say, access token have 1 hour expiry. How do we have mechanism to get a new token before the token is expired?
Any comments would be very appreciated. Thank you in advance.
It sounds like you would benefit from a little singleton object that manages the token for you. You can create an interface for getting the token that does the following:
If no relevant token in the cache, go get a new one and return a promise that will resolve with the token. Store that promise in the cache in place of the token.
If there is a relevant token in the cache, check it's expiration. If it has expired or is about to expire, delete it and go to step 1. If it's still good, return a promise that resolves with the cached token (this way it always returns a promise, whether cached or not).
If the cache is in the process of getting a new token, there will be a fresh token stored in the cache that represents the future arrival of the new token so the cache can just return that promise and it will resolve to the token that is in the process of being fetched.
The caller's code would look like this:
tokenCache.getToken().then(token => {
// use token here
});
All the logic behind steps 1, 2 and 3 is encapsulated inside the getToken() method.
Here's an outline for a tokenCache class that hopefully gives you the general idea:
const tokenExpiration = 60 * 60 * 1000; // 1 hr in ms
const tokenBeforeTime = 5 * 60 * 1000; // 5 min in ms
class tokenCache {
constructor() {
this.tokenPromise = null;
this.timer = null;
// go get the first token
this._getNewToken().catch(err => {
console.log("error fetching initial token", err);
});
}
getToken() {
if (this.tokenPromise) {
return this.tokenPromise().then(tokenData => {
// if token has expired
if (tokenData.expires < Date.now()) {
return this._getNewToken();
} else {
return tokenData.token;
}
});
} else {
return this._getNewToken();
}
}
// non-public method for getting a new token
_getNewToken() {
// for example purposes, this uses the got() library to make an http request
// you fill in however you want to contact the identity provider to get a new token
this.tokenPromise = got(tokenURL).then(token => {
// make resolve value be an object that contains the token and the expiration
// set timer to get a new token automatically right before expiration
this._scheduleTokenRefresh(tokenExpiration - tokenBeforeTime);
return {
token: token,
expires: Date.now() + tokenExpiration;
}
}).catch(err => {
// up error, clear the cached promise, log the error, keep the promise rejected
console.log(err);
this.tokenPromise = null;
throw err;
});
return this.tokenPromise;
}
// schedule a call to refresh the token before it expires
_scheduleTokenRefresh(t) {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
this._getNewToken().catch(err => {
console.log("Error updating token before expiration", err);
});
this.timer = null;
}, t);
}
}
How do we handle race condition when there are concurrent request to service A that can cause multiple requests are sent to get access token and have multiple updates to the cache?
You store a promise and always return that promise. Whether you're in the middle of getting a new token or there's already a token in that promise, it doesn't matter. You return the promise and the caller uses .then() or await on the promise to get the token. It "just works" either way.
Let say, access token have 1 hour expiry. How do we have mechanism to get a new token before the token is expired?
You can check the token for expiration when it's requested and if it's expired, you replace the existing promise with one that represents a new request for the token.

How do I refresh a Smartcar access token?

I use the Smartcar API on my Tesla (https://teslaapi.dev/) and successfully made a request before but I think the access token expired and I don't know how to refresh it.
I followed this guide: https://smartcar.com/docs/integration-guides/express/request/
It talks about the access token but it doesn't tell me how to get the refresh token.
// ./index.js
app.get('/vehicle', function(req, res) {
// TODO: Request Step 2: Get vehicle information
return smartcar.getVehicleIds(access.accessToken)
.then(function(data) {
// the list of vehicle ids
return data.vehicles;
})
.then(function(vehicleIds) {
// instantiate the first vehicle in the vehicle id list
const vehicle = new smartcar.Vehicle(vehicleIds[0], access.accessToken);
return vehicle.info();
})
.then(function(info) {
res.render('vehicle', {
info: info,
});
});
});
This doesn't work anymore:
{
"error": "authentication_error",
"message": "Invalid or expired token provided."
}
I think it's because I need to replace the accessToken with a refresh token. How can I do this?
So your hunch about needing to use the refresh token is correct.
If you look at the "Request access token" section of API Reference, it notes that the access tokens are only valid for 2 hours, after that point you'll need to use the refresh token to get a new access token to use.
If you're using the Node SDK, you can use the exchangeRefreshToken method to exchange your refresh token for a new set of tokens.
Here's an example with all of that integrated:
// ./index.js
app.get('/vehicle', function(req, res) {
if (smartcar.isExpired(acccess.expiration)) {
const
}
// TODO: Request Step 2: Get vehicle information
return smartcar.getVehicleIds(access.accessToken)
.then(function(data) {
// the list of vehicle ids
return data.vehicles;
})
.then(function(vehicleIds) {
// instantiate the first vehicle in the vehicle id list
const vehicle = new smartcar.Vehicle(vehicleIds[0], access.accessToken);
return vehicle.info();
})
.then(function(info) {
res.render('vehicle', {
info: info,
});
});
});
For a proper long term implementation of this you'll need to involve some sort of database that stores the access object based on vehicle id.

Convert NodeJS asynchronous code to Spring Project Reactor

I have below NodeJS code:
// req and resp are http request, response objects
var uri = req.getURI()
var pageView = new PageView(uri)
var token = req.token
if (token) {
UserRepository.findByToken(token, function(notFound, user){
if(notFound) { // means user not found by specified token
var newUser = new User('John Doe')
user.foo = 'some value'
processUser(newUser, pageView)
} else { // user found by token
user.foo = 'some value'
processUser(user, pageView)
}
})
} else { // token does not exist
token = new Token('some value')
resp.setToken(token)
var newUser = new User('John Doe')
user.foo = 'some value'
processUser(newUser, pageView)
}
processUser(user, pageView) {
PageViewRepositiry.save(pageView, function(error, savedPageView){
if(error) {
throw 'error'
}
user.pageViews.push(savedPageView)
// save the modified savedUser
UserRepository.save(user , function(error, savedUser){
})
})
}
It uses Repository pattern as abstraction over database layer (same as the Repository pattern in Spring applications).
Basically it finds user by incoming token (from http req object). If user is found then updates user entity and adds the saved pageView entity and saves the modified user. If user is not found by token then it creates a new User, updates the user with saved pageView, saves the user.
How the same code will be written in Spring Project Reactor (Flux) ?
Is it possible to solve this problem without using block()? Ideally I would like a solution that does not use block().
First of all, you have some logic to generate a token if a token isn't present. For example:
private Mono<String> getToken(String token) {
return Mono
.just(token)
.switchIfEmpty(Mono.just("some token"));
}
In this case, it's a bit overkill to use switchIfEmpty for this, but I assume your process to generate a token is a bit more complex, otherwise you could have worked with Optional<String> in stead (eg. token.orElse("some token")).
Additionally, we also have some logic to either find the user by its token, or create a new user if there is no user by the given token:
private Mono<User> findUserByToken(String token) {
return userRepository
.findByToken(token)
.switchIfEmpty(userRepository.save(new User("John Doe", token)));
}
Now that we have these methods, we can create a PageView and use these methods along the way. The reason I start with creating a PageView is because that's the first "constant" in the entire token, regardless of whether there is a token/user found:
return Mono
.just(new PageView(uri))
.flatMap(pageViewRepository::save)
.flatMap(pageView -> getToken(token)
.flatMap(this::findUserByToken)
.doOnNext(user -> user.setFoo("foo"))
.doOnNext(user -> user.getPageView().add(pageView)))
.flatMap(userRepository::save)
.map(User::getToken);
Now, since you need the token to add to the response, and I figured out that the token is part of the User object somehow (otherwise UserRepository.findByToken() wouldn't work?), it would be easier to just use User::getToken at the end to retrieve the token to pass to the response.
Be aware though, the repository pattern does work properly with Spring, but there is only reactive support for MongoDB, Cassandra, Couchbase and Redis. Other than that there's also reactive support for PostgreSQL through rdbc, but I don't think Spring data has support for that.

How to renew a session of salesforce using jsforce?

I am using jsforce node module for doing CRUD operation in salesforce.
For making a connection to salesforce, I have following input
username, password, securityToken and loginUrl.
Here's how I make a connection first time.
var conn = new jsforce.Connection({
loginUrl: connectionDetails.salesforce.loginUrl
});
conn.login(connectionDetails.salesforce.username,
connectionDetails.salesforce.password + connectionDetails.salesforce.securityToken,
function(err, userInfo) {
if (!err) {
console.log('User with user id ' + userInfo.id + ' successfully logged into Salesforce');
successCb(conn.accessToken, conn.instanceUrl);
} else {
console.log('Login failed to https://test.salesforce.com/');
errorCb('Login failed to https://test.salesforce.com/');
}
});
I store the accessToken and Instanceurl in the req object provided by Express.
After that any CRUD operation I perform like below
var salesConn = new jsforce.Connection({
accessToken: salesforceAccessToken,
instanceUrl: salesforceInstanceUrl
});
salesConn.sobject('Lead').retrieve(someLeadID, function(err, data) {
...
});
Now suppose I keep my server idle for few hours or may be even a day, then if I do a CRUD operation then the call fails. This I am pretty sure that the session has expired.
Now I have two queries
Is the above correct way of making connection to salesforce using the input connection details I have?
How can I know that the session has expired and make a new session?
PS
I tried to look into the Access Token with Refresh Token, but that is only available with OAuth2 authorization code flow.
A little late, but I had the same issue today and I fixed it by calling conn.login(username, password+token) whenever I get an invalid session error.
I am doing something different though, I'm not creating a second variable to use with my SF calls, but instead use the original conn variable, conn.sobject(...).
It would refresh token automatically.
my jsforce version is "jsforce": "^1.4.1"
jsforce has a _refreshDelegate
Connection.prototype.login = function(username, password, callback) {
// register refreshDelegate for session expiration
this._refreshDelegate = new HttpApi.SessionRefreshDelegate(this, createUsernamePasswordRefreshFn(username, password));
if (this.oauth2 && this.oauth2.clientId && this.oauth2.clientSecret) {
return this.loginByOAuth2(username, password, callback);
} else {
return this.loginBySoap(username, password, callback);
}
};

Resources