I'm new to RxJS and am asking for conceptual help on how to approach the following process:
Backend is secured with short lived JWT access tokens. Upon authentication, client is issued initial access token and long lived refresh token. Refresh token can be used to create a new access token.
The client can decode the tokens and knows if a token is expired or not. The client shall refresh access tokens lazily when they are expired; not retry a a failing request.
The client shall not issue multiple refresh-requests simultaneously.
The last part I struggle with. I imagine a semaphore, or "gate" of some kind:
The first refresh-request passes through the gate and locks it behind itself. It then initiates the actual token-refresh.
Other refresh-requests are blocked at the gate, waiting to pass.
The completion of the token-refresh stores the new access token and lifts the gate.
All refresh-requests return the new access token.
I found this code that uses redux to implement the gate. Is that necessary?
Alternatively, could the gate be implemented by a BehaviorSubject?
I found a solution! The code is available here.
The gate is using a BehaviorSubject to have a singleton holding the state. By applying a filter I get a blocking gate that will allow passage when the underlying gate is lifted:
const gate$ = new BehaviorSubject(true); // open at first
const openGate$ = gate$.pipe(
filter(x => x === true),
take(1) // take only one event, then complete
);
// wait for the gate to open
openGate$.subscribe(() => {
// do something
});
// close the gate:
gate$.next(false);
// and open it again
gate$.next(true);
By using a BehaviourSubject, the gate is initialised and open by default.
Related
I'm developer my first symfony (3) app. it is a REST service publicly accessible.
I'm doing this using FOSRestBundle.
I'll have to ad some admin forms soon or later, and I'll probably want to create them directly (without passing by the extra work of consuming my own web services)
I wonder how to handle the CSRF token in this case. I see different solutions:
globally deactivate the CSRF token : I don't want to do this
create two set of forms, one with the token activated : form my admin forms, the other one for the REST API. => in this case, the rest API can't have a fallback _format=html
find a way to give the api consumer an auth, with an API_GROUP, and disable the token for this group
it seem to me the best solution, but I don't know how to do it transparently, without affecting the auth of my future admin, and without needing to give credentials in the REST request.
use an event listener in order to hack symfony's auth mechanism and give an auth if a call is made to the REST API (all but _format=html)
Which one of this (or other) solution seem the best to you, and how would you code it?
I found a way, perhaps not the best one, but it works :
$_format = $request->attributes->get('_format');
if ('html' == $_format) {
$form = $this->createForm(ItopInstanceUserType::class, $itopInstanceUser);
} else {
$form = $this->createForm(ItopInstanceUserType::class, $itopInstanceUser, ['csrf_protection' => false]);
}
For me, forget CSRF token managed by yourself, check subjects like Oauth authentication.
Take a look here: https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/blob/master/Resources/doc/index.md
FOSOAuthServerBundle works perfectly with FOSRestBundle.
I am creating a website using Parse, and I have run into an issue. When I let my browser sit around for a couple hours, calls to the graph API don't seem to work anymore. The response from the graph api is:
Error validating access token: Session has expired on Wednesday,
29-Apr-15 00:00:00 PDT. The current time is Wednesday, 29-Apr-15
21:34:32 PDT.
Of course, my token has expired... but now I can't find a way to easily refresh my access token without sending the user back through the login process... which isn't an ideal workflow.
A glimmer of hope in the Facebook Javascript documentation has me wondering if I am potentially just doing something wrong. If not, their documentation is horribly misleading.
Also keep in mind that the access tokens that are generated in
browsers generally have a lifetime of only a couple of hours and are
automatically refreshed by the JavaScript SDK. If you are making calls
from a server, you will need to generate a long lived token, which is
covered at length in our access token documentation.
source: https://developers.facebook.com/docs/facebook-login/login-flow-for-web/v2.3#token
What can we do to automatically refresh this token?
The JS SDK should automatically provide fresh access tokens, yes – although I am not sure if it does so if you just have the app open for a couple of hours without interacting with it (meaning: reloading the page, so that the JS SDK gets re-initialized as well). Since this is not a very common use case, it might not.
I would suggest that you try and use FB.getLoginStatus to get a fresh access token. (There should be no need to then set this access token explicitly on subsequent API calls; that the SDK will handle itself.)
This method uses an internally cached result, to avoid having to make a request to Facebook every time it is called. Since the expiry time of a token is known, I would expect the SDK to be able to decide itself if an actual request is necessary again – but if it doesn’t, you can also force a new request by setting the second parameter to true. (This is however not recommendable for every call, so I would try and see if calling the method without it is able to resolve the problem already.)
A simple workaround is to make an API call before the current access_token expired.
Let's say the token expires every 2 hours, you can, for example, make a simple /me call every 1H30 :
/**
* Make an API Call to refresh the access_token
*/
function tokenRefresh() {
FB.api("/me", function (response) {
// the token is refreshed
});
}
// call it every 1H30
var tokenTimeout = window.setTimeout(tokenRefresh, 5400000);
If you don't want that code be executed when your user uses the app, so when it will be useless, you can, each time you make a request, reset the timeout in the callback :
window.clearTimeout(tokenTimeout);
var tokenTimeout = window.setTimeout(tokenRefresh, 5400000);
Therefore, your app makes a call each 1H30 that passed without an API call.
I was trying out the new enhanced revocable sessions in Parse on my Android app. It works well when logging in or signing up via email password or facebook but doesn't work well for custom authentication, e.g. google+.
I'm currently logging in the user using the cloud code which also creates the new user when signing up. This does not create a new Session object, that means the new enhanced sessions are not used and it still uses the legacy sessions.
I pass the session token back to client where using the become method the user logs in but it's the legacy sessions.
This feels like the feature is not complete but I would really like to move to the new enhanced sessions with my app. Has anyone worked with them yet? Are there any workarounds using the REST API or by creating the sessions manually and handling them manually? I looked into the JS API but it says it's only read only.
Here's the Blog post on Enhanced Sessions.
Where should I go next?
Yes, I found a solution but it's a workaround, works for my case because I don't support signing up with user/password.
Basically, the solution (cloud code) in semi pseudo-code is:
Fetch the user with master key
Check if user.getSessionToken() has value
if it has, return the session token and do a user.become() in the client as usual
if it's not, here the workaround, do the following:
yourPreviousPromiseInOrderToChainThem.then(function(user)
password = new Buffer(24);
_.times(24, function(i) {
password.set(i, _.random(0, 255));
});
password = password.toString('base64')
user.setPassword(password);
return user.save();
}).then(function(user) {
return Parse.User.logIn(user.get('username'), password)
}).then(function(user) {
var sessionToken = user.getSessionToken();
// Return the session token to the client as you've been doing with legacy sessions
})
That means, I'm changing the user password each time in order to make a remote login and, of course, I know thist can't be applied to all cases, it's enough for app because I don't support login with user/password (only third party logins) but I understand that maybe it's not for all cases.
I got the idea from this official Parse example.
I don't like this solution because I think is not a workaround, it's a mega hack but I think there is no other way to do it currently (either Parse.com or Parse-Server)
If you find other workaround, please, share it :)
Well there seems to be quite a bit of confusion on this topic and I am struggling to get a clear answer, so here is my question...
I am using the serverside flow to obtain access tokens for my web app, I previously used offline_access which is now being depreciated so I need a way to refresh the token in the following situations:
1) User changes FB password
2) Token expires naturally
My app posts results to users FB walls so the refresh needs to be done automatically by our server (no cookies or OAuth dialogs)
I thought I could try to use the new endpoint described here
http://developers.facebook.com/roadmap/offline-access-removal/
, with the following piece of code (Java):
public static String refreshFBAccessToken(String existingAccessToken)
throws Exception{
//Currently not working
String refreshUrl = "https://graph.facebook.com/oauth/access_token?
client_id="+FacebookApp.appId+"
&client_secret="+FacebookApp.appSecret+"
&grant_type=fb_exchange_token
&fb_exchange_token="+existingAccessToken;
URL url = new URL(refreshUrl);
URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(),
url.getQuery(), null);
String result = readURL(uri.toURL());
String[] resultSplited = result.split("&");
return resultSplited[0].split("=")[1];
}
But this doesnt seem to work (I get a response 400), and when I re-read the documentation it seems this endpoint is used for tokens obtained using the client-side flow only...
So what about the serverside flow....?
Can someone tell me if the approach above is correct or there is another way?
Many thanks
From what I understand there is no server side flow for refreshing tokens.
The refresh token call needs to include the response of the user authentication process which is a short lived token.
You will need to include the refresh token process as part of the user login flow or if this doesn't work for your setup you will need to email the user asking them to come back!
I dont know java but syntax is very much like C#, so I can say,you are doing everything right.
But I doubt what does this function readURL do ?
If it works like get_file_contents() of php (i.e. if it does an HTTP get) , I guess thats not a right way to do .
Based on my experience on google's refresh token method, I think you should do an HTTP POST instead of HTTP GET to given url.
I'm writing a Catalyst application that's required to have a fairly short session expiration (15 minutes). I'm using the standard Catalyst framework authentication modules, so the user data is stored in the session -- i.e., when your session expires, you get logged out.
Many of the uses of this application will require >15 minutes to complete, so users will frequently submit a form only to find their session state is gone and they're required to log back in.
If this happens I want to preserve the original form submission, and if they log in successfully, continue on and carry out the form submission just as if the session had not expired.
I've got the authentication stuff being handled by an auto() method in the controller -- if you request an action that requires authentication and you're not currently logged in, you get redirected to the login() method, which displays the login form and then processes it once it's submitted. It seems like it should be possible to store the request and any form parameters when the auto method redirects to the login(), and then pull them back out if the login() succeeds -- but I'm not entirely sure of the best way to grab or store this information in a generic/standard/reusable way. (I'm figuring on storing it in the session and then deleting it once it's pulled back out; if that seems like a bad idea, that's something else to address.)
Is there a standard "best practices" or cookbook way to do this?
(One wrinkle: these forms are being submitted via POST.)
I can't help thinking that there's a fundamental flaw in mandating a 15 minute timeout in an app that routinely requires >15 minutes between actions.
Be that as it may, I would look at over-riding the Catalyst::Plugin::Session->delete_session method so that any contents of $c->request->body_parameters are serialised and saved (presumably to the database) for later recovery. You would probably want some rudimentary check of the POST arguments to ensure they're what you're expecting.
Similarly, create_session needs to take responsibility for pulling this data back out of the database and making it available to the original form action.
It does seem like a messy situation, and I'm inclined to repeat my first sentence...
UPDATE:
Whether you use delete_session or auto, the paradoxical issue remains: you can't store this info in the session because the time-out event will destroy the session. You've got to store it somewhere more permanent so it survives the session re-initialization. Catalyst::Plugin::Session itself is using Storable, and you should be able to with something along these lines:
use Storable;
...
sub auto {
...
unless (...) { #ie don't do this if processing the login action
my $formitems = freeze $c->request->body_parameters;
my $freezer = $rs->update_or_create(
{user => $c->user, formitems => $formitems} );
# Don't quote me on the exact syntax, I don't use DBIx::Class
}
...
my $formitems = $c->request->body_parameters
|| thaw $rs->find({$user => $c->user})->formitems
|| {} ;
# use formitems instead of $c->request->body_parameters from here on in
The underlying table probably has (user CHAR(x), formitems TEXT) or similar. Perhaps a timestamp so that nothing too stale gets recovered. You might also want to store the action you were processing, to be sure the retrieved form items belong to the right form. You know the issues for your app better than me.
I would store the form data as some sort of per user data in the model.
Catalyst::Plugin::Session::PerUser is one way of doing that (albeit somewhat hackishly). I would reccomend using the session plugin only for authentication and storing all the state info in the model that stores your user data instead.
And I totally agree with RET's opinion that the 15 minute limit seems really counter productive in this context.
I came across this whilst searching CPAN for something entirely unrelated.
Catalyst::Plugin::Wizard purports to do exactly what you need. The documentation suggests it can redirect to a login page whilst retaining the state of the previous action.
NB: I haven't used it, so can't vouch for its effectiveness.
In the end, we ended up grabbing the pending request (URL+params) in the auto(), serializing and encrypting it, and passing it via a hidden form element on the login page. If we got a login request with the hidden element populated, we decrypted and deserialized it and then redirected appropriately (making sure to pass through the standard "can this user do this thing" code paths).
You could always have some javascript on the client that keeps the session from expiring by making a small request every few minutes.
Or you could have AJAX check for an active session before posting the form and presenting the user with a new login box at that time if needed.