Handling JWT token expiration in a React based application? - rest

We are in the process of building a web based application, using React and making use of a REST based server. The question that came up is how to best manage a previously authenticated connection becoming unauthenticated and whether there are any React or React/Redux design patterns for this?
Previous designs had used sessions, but since the REST server is meant to be stateless, the current REST server design will probably look at follows:
POST /auth/authenticate --> Authenticate and provides a token
DELETE /auth/token --> Invalidates the token, black-list token
PUT /auth/token --> Renews token, if still valid
GET /auth/token --> Indicates if token is still valid
On the client side the current draft design is to wrap the fetch function with our own function for dealing with checking the response state and then either calling a client side service for invalidating state and redirecting to login page or doing the same thing with Redux. We are also looking to add an interval timer to check the token we have and then automatically do the same thing if the 'exp' field is present and past.
The function we have at this point is (it does not take care of revalidation of token, at this point, to prevent an activate 'session' expiring):
function handleFetchResponse(fetchResponse) {
let newResponse;
try {
return fetchResponse.then((response) => {
newResponse = response;
const contentType = response.headers.get('content-type');
if (contentType && contentType.indexOf("application/json") !== -1) {
return response.json();
} else if (response.status === 200) {
return response.blob();
} else {
if (response.status === 401) {
forceLogout();
}
throw new RequestError(response.statusText, response.status);
}
}).then((responseBody) => {
if (newResponse.status === 401) {
forceLogout();
return;
} else if (newResponse.status !== 200) {
if (responseBody) {
throw new RequestError(
responseBody.details ||
newResponse.statusText,
newResponse.status
);
}
throw new RequestError(newResponse.statusText, newResponse.status);
}
return responseBody;
});
} catch (error) {
console.error('handleFetchResponse:error', error);
return Promise.resolve(err)
.then((err) => {
throw err;
})
}
}
It would be called via:
return handleFetchResponse( fetch(...) );
Is this an acceptable design or are they ways to improve on this?

Related

Google Action Webhook Inline Editor Returns Before the API call

This is my first Google Action project. I have a simple slot after the invocation. User enters the value on prompt and slot invokes the webhook and make a call to API using the user input. All works fine. However the webhook returns to users even before the API call finish processing and returns the value (line 1 conv.add). I do see in the logs that everything from API is logged fine after the webhook returns to user. Below is the code I am using. I am using inline editor. What am I missing? Thanks for help in advance.
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
var https = require('https');
const fetch = require('node-fetch');
const app = conversation({debug: true});
app.handle('SearchData', conv => {
const body = JSON.stringify({
val: "this is my body"
});
// prepare the header
var postheaders = {
'Content-Type' : 'application/json',
'Auth' : 'MyAuthCreds'
};
fetch('https://host.domain.com/data', {
method: 'post',
body: body,
headers: postheaders,
})
.then(res => res.json())
.then(d => {
console.log(d);
var profile = d;//JSON.parse(d);
console.log(d.entries);
console.log("Length: "+ d.entries.length);
if(d.entries.length > 0)
{
console.log("Data found");
conv.add("Data found"); //line 1
}
else
{
console.log("no data found");
conv.add("no data found"); //line 1
}
})
.catch(function (err) {
// POST failed...
console.log(err);
});
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
Your issue is that your handler is making API calls which are asynchronous, but the Assistant Conversation library doesn't know that you're doing so. So as soon as the handler finishes, it tries to send back a response, but your asynchronous responses (the stuff in the then() blocks) haven't executed yet.
To address this, you need to return a Promise object so the library knows to wait till the Promise is fulfilled before it returns.
Fortunately, in your case, this should be pretty straightforward. fetch and all the .then() blocks return a Promise. So all you need to do is add a return statement in front of the call to fetch. So something like this:
return fetch('https://host.domain.com/data', {

Facebook Connection Failed to GameSparks Server in Unity

I'm setting up the Facebook Connection to GameSparks server with FacebookConnectRequest using Facebook SDK for Unity. However, I'm getting an error response with key "accessToken" and value "NOTAUTHENTICATED". The details of the error is "The system was unable to authenticate the token.".
I have tried to reimport Facebook and GameSparks SDK in Unity. Change the some initialization of the Facebook and GameSpark in the code. However, I could not come up with a solution.
public void ConnectPlayerViaFacebook()
{
ChangeCurrentText("Connecting Facebook With Server...");
Debug.Log("Connecting Facebook With GameSparks...");// first check if FB is ready, and then login //
// if it's not ready we just init FB and use the login method as the callback for the init method //
if (!FB.IsInitialized)
{
ChangeCurrentText("Initializing Facebook...");
Debug.Log("Initializing Facebook...");
FB.Init(ConnectGameSparksToGameSparks, null);
}
else
{
FB.ActivateApp();
ConnectGameSparksToGameSparks();
}
}
///<summary>
///This method is used as the delegate for FB initialization. It logs in FB
/// </summary>
private void ConnectGameSparksToGameSparks()
{
if (FB.IsInitialized)
{
FB.ActivateApp();
Debug.Log("Logging into Facebook...");
ChangeCurrentText("Logging into Facebook...");
var perms = new List<string>() { "public_profile", "email" };
FB.LogInWithReadPermissions(perms, (result) =>
{
if (FB.IsLoggedIn)
{
ChangeCurrentText("Logged in, Connecting Server via Facebook...");
new FacebookConnectRequest()
.SetAccessToken(AccessToken.CurrentAccessToken.TokenString)
.SetDoNotCreateNewPlayer(false)
.SetDoNotLinkToCurrentPlayer(false)
.SetSwitchIfPossible(false)
.SetSyncDisplayName(true)
.Send((fbauth_response) =>
{
if (!fbauth_response.HasErrors)
{
...
}
else
{
Debug.Log(fbauth_response.Errors.JSON.ToString());
ChangeCurrentText("Server Authentication with Facebook Failed!" + fbauth_response.Errors.JSON.ToString());
}
});
}
else
{
Debug.LogWarning("Facebook Login Failed!" + result.Error.ToString());
ChangeCurrentText("Facebook Login Failed!" + result.Error.ToString());
}
});
}
else
{
ConnectPlayerViaFacebook(); // If we are still not connected, then try to process again
}
}
I want to remove the error response of the FacebookConnectRequest of the GameSparks request.
Thanks to derHugo's advice I have solved the problem. For some reasons the acces token is broken before the FacebookConnectionRequest. To prevent any undesirable situation about access token it needs to be manually refreshed. To do it, FB.Mobile.RefreshCurrentAccessToken needs to be used before FacebookConnectionRequest.
The explanation of that function is as follows: It may be desireable to manually refresh the current access token granted to the application by the user in order to retrieve up-to-date permissions, and extend the expiration date, if extension is possible. Use FB.Mobile.RefreshCurrentAccessToken to accomplish this. (source)
if (FB.IsLoggedIn)
{
FB.Mobile.RefreshCurrentAccessToken(callback =>
{
...
});
ChangeCurrentText("Logged in, Connecting Server via Facebook...");
new FacebookConnectRequest()
.SetAccessToken(AccessToken.CurrentAccessToken.TokenString)
.SetDoNotCreateNewPlayer(false)
.SetDoNotLinkToCurrentPlayer(false)
.SetSwitchIfPossible(false)
.SetSyncDisplayName(true)
.Send((fbauth_response) =>
{
...
});
}
else
{
...
}

haw load user information after login SPA koa.js

I realized the authorization of the user through the passport (Koa, Mongodb, React, Redux).
router.post('/login', function(ctx, next) {
return passport.authenticate('local', async function(err, user, info) {
if (err) throw err;
if (user === false) {
ctx.status = 401;
ctx.body = { error: info };
} else {
ctx.body = {
success: true
};
await ctx.login(user);
}
})(ctx, next);
})
If the user logged in, he redirects to the profile page(main page).
router.get('/login', function(ctx, next) {
if (ctx.isAuthenticated()) {
ctx.redirect('/');
} else {
ctx.body = fs.readFileSync(path.resolve(path.join('build', 'index.html')), 'utf8')
}
});
Since I have a spa I always pass a static file (routing on the client via a react-router)
The problem is that I can not understand how I get information about the user when the profile page is loaded
If I send a ajax-request (feth) from the React-component, then the request for another session id and it is not associated with the user.
Or, I need to do this on the server, but how I might to use the store storage on the server?
How best to solve this problem?
Well, while it might not be the best way, the easiest way to do this would be to have a section in your html file like..
<script type="text/javascript">
window.currentUser = $$USER$$
</script>
And then do a replace on the body.
let content = fs.readFileSync(path.resolve(path.join('build', 'index.html')), 'utf8')
content = content.replace('$$USER$$', JSON.stringify(user))
ctx.body = content
In your React app, you can use window.currentUser.

Firebase automatic login after reloading Ionic app/page

Back when I was using Parse, it seemed like the SDK would store the session data locally and the user didn't have to log in again after refreshing the page (or exiting the mobile app). This doesn't seem to be the case with Firebase/Angularfire; everytime I refresh my web page, the authentication data gets lots. This seems like really basic and important functionality that I would be surprised the awesome people at Firebase haven't implemented. Am I missing something?
For completeness; here is my code in app.run():
// ASG june 2016 - Upgrade firebase SDK
firebase.initializeApp(FirebaseConfig);
// login as anonymous if not already logged in
var currentUser = $firebaseAuth().$getAuth();
if (currentUser) {
console.log("Signed in as:", currentUser);
} else {
console.log("Not logged in; going to log in as anonymous");
$firebaseAuth().$signInAnonymously().then(function(authData) {
console.log("Signed in anonymously as:", authData.uid);
}).catch(function(error) {
console.error("Anonymous authentication failed:", error);
});
}
// register the on auth callback
$firebaseAuth().$onAuthStateChanged(function(authData) {
if (authData) {
console.log("Logged in as:", authData.uid);
if(typeof($rootScope.userProfile) == "undefined"){
$rootScope.userProfile = FirebaseProfileService.getUserProfile(authData.uid, false);
}
}
});
Firebase absolutely presists authData and has a lot of goodness you hardly find elsewhere. Therefore, I don't see any logical reason behind calling getAuth() here as you can easily get the authData from the onAuthStateChanged listener. In this case, you may need to remove your operations of getting currentUser using getAuth() and move your ELSE condition to onAuthStateChanged if you want to sign up users as anonymous in case no AuthData was found, and then you will be good to go, I hope. Hope that works.
The auth token is persisted between page/app reloads. But it will often need to get a new refresh token when the page reloads, which requires a round-trip to the Firebase servers. Since this takes time, the initial getAuth() may return null while that process is ongoing.
var currentUser;
$firebaseAuth().$onAuthStateChanged(function(authData) {
if (authData) {
console.log("Logged in as:", authData.uid);
currentUser = authData.currentUser;
if(typeof($rootScope.userProfile) == "undefined"){
$rootScope.userProfile = FirebaseProfileService.getUserProfile(authData.uid, false);
}
}
else {
console.log("Not logged in; going to log in as anonymous");
currentUser = null;
$firebaseAuth().$signInAnonymously().catch(function(error) {
console.error("Anonymous authentication failed:", error);
});
}
});

Openfire in-band-registration via Bosh not working with Strophe/Strophe.register.js

I previously asked a similar question about ejabberd, however ejabberd was giving other problems, so I switched to openfire. For the sake of not making the original qestion to cluttered, I decided to create a new question, since this question pertains to openfire and is a different issue than the one I was having with ejabberd.
So, here goes the question:
I have a strophe.js xmpp client, which connects to an openfire 3.10.0 alpha server running on the Amazon cloud. I need 3.10.0 alpha over 3.9.3 because of a bfix which is included in the former, but not the latter.Anyway, since this is a strophe client, I have enabled bosh, and I can see it running at myAWSDNS.com:7070. I am able to connect to the server via my client using this bosh service and existing accounts, and send messages back and forth so it seems to be functioning ok.
I would also like to add in-band registration, for which I use strophe.register.js
This is the code I use for this:
var tempConn = new Strophe.Connection("http//myAWSDNS.com:7070/http-bind/");
tempConn.register.connect("myAWSDNS.com", function (status) {
if (status === Strophe.Status.REGISTER) {
// fill out the fields
connection.register.fields.username = "juliet";
connection.register.fields.password = "R0m30";
// calling submit will continue the registration process
connection.register.submit();
} else if (status === Strophe.Status.REGISTERED) {
console.log("registered!");
// calling login will authenticate the registered JID.
connection.authenticate();
} else if (status === Strophe.Status.CONFLICT) {
console.log("Contact already existed!");
} else if (status === Strophe.Status.NOTACCEPTABLE) {
console.log("Registration form not properly filled out.")
} else if (status === Strophe.Status.REGIFAIL) {
console.log("The Server does not support In-Band Registration")
} else if (status === Strophe.Status.CONNECTED) {
// do something after successful authentication
} else {
// Do other stuff
}
});
This seems to work fine, as it enters the first if-bracket (status === Strophe.Status.REGISTER), and tries to set connection.register.fields.username = "juliet";
However, here, when executing that line, it jumps into strophe.js line 2476:
if (this.connect_callback) {
try {
this.connect_callback(status, condition);
} catch (err) {
Strophe.error("User connection callback caused an " +
"exception: " + err);
}
}
where 2476 is the code in the catch(err) { ...... } bracket.
If I inspect err, this is what I get:
So message: connection is not defined and, obviously, the regstration doesnt work, and I am not sure why. Does anyone have any input on this?
Thanks, and best regards,
Chris
You might not like this answer... The reason for connection == undefined is because you named it tempConn.