FlowRouter, how to get data when routing depends on subscription - meteor

I'm writing a licence validation part for my application and want to redirect the user to a renewal page if and only if their licence has expired.
I am using FlowRouter and Blaze.
All my authenticated routes are in a group:
let authenticated = FlowRouter.group({
triggersEnter: [checkAuthenticated, checkSubscription]
});
I then check if the subscription is valid like so:
const checkSubscription = function(context){
let path = FlowRouter.current().path;
if (!Meteor.userId()){
return;
}
const sub = new Subscription();
if (sub.isInvalid() && path !=="/manage-practice/subscription"){
FlowRouter.go("/manage-practice/subscription");
}
};
My class subscription uses a collection that I can only load once a user has logged in. My problem is that the router usually triggers this redirection before this data has been loaded.
Is there a best practice approach to solve this?

Redirect with Triggers
I'm not sure about this being 'best practice' but one approach is to use the Flow Router redirect functionality on your login event.
You can see examples at: https://atmospherejs.com/kadira/flow-router#redirecting-with-triggers and https://github.com/meteor-useraccounts/flow-routing.
The initial login path (using Accounts.onLogin();) could be to a generic 'loading...' template while you evaluate the user's collection. On a callback you can then use the custom redirect function to either redirect to the requested page in your app, or redirect the user to your '/manage-practice/subscription' path.
FlowRouter.wait()
I have to confess I wasn't previously familiar with this second option, but I've just come across FlowRouter.wait(). This can be useful to delay the default routing process until some other evaluation is complete. I suspect this might only be relevant if a user logs directly into a page within your authenticated routing group.
Documentation: https://atmospherejs.com/kadira/flow-router#flowrouter-wait-and-flowrouter-initialize

Related

Using OAuth2 in Blazor and redirect user based on claims to certain page after successfull login

I use keycloak (OAuth2/Open ID Connect) to authorize and authenticate users in a Blazor Server Side application. Furthermore I use the services.AddAuthentication().AddOpenIdConnect() extension from Microsoft.
After successfull login I want to redirect the user to a certain page based on its claims. So f.e. if the user has the claim "gis" he should be redirected to "/gis" or if he has the claim "claim123" he should be redirected to page "/page456".
Right now I solve this problem by overriding OnInitialized in the MainLayout.razor and look if the user has a certain claim and then redirect him with the help of the NavigationManager.
#inject NavigationManager navMan;
//...
protected override void OnInitialized()
{
if (_user.HasClaim(t => t.Type == "claim123"))
{
navMan.NavigateTo("/page456");
}
}
The problem with this approach is, that it will first load the default index home page ( "/" ) and then after a short period of time will reload to the new destination. This "flickering" is not very nice and I thought there must be a better way to directly redirect after successful "/openid-connect" redirection. Can I may use some kind of middleware or one of the OpenIdConnectEvents?

How to get expiration time for silent token in Oidc-Client

I have SPA developed application on which I used to implement Oidc-Client for OAUTH authentication and below are the clarifications.
How to configure silent-refresh page with web pack config file in angular structure based project since silent-refresh.html is not invoked on token expiration.
Even if silent token generated then how to get/set expiration time of silently generated token?
Kindly help and suggest.
SILENT REFRESH
Rather than a separate HTML page, my personal preference is to handle this by a silent token renewal response to the index.html page. Then write code like this:
if (window.top === window.self) {
// Run the main app
const app = new App();
app.execute();
} else {
// If our SPA is running on an iframe, handle token renewal responses
const app = new IFrameApp();
app.execute();
}
I find that this approach avoids adding complexity to the WebPack / build system. The code for the iframe app does very little other than receiving the silent token renewal response.
EXPIRY
Interesting why you want to use access token expiry times directly. You can get the value like this:
const user = await this._userManager.getUser();
if (user) {
console.log(user.expires_at);
}
The real requirement here is to ensure that you avoid errors for end users when an API call fails due to an expired access token. This is best handled via the following actions:
If an API call fails with a 401 status code
Then try to get a new access token, generally via userManager.signInSilent()
Then retry the API call with the new access token
Therefore the way you call APIs should have a helper class with some retry logic, as in my example here.
To get notified after silent refresh, add an event handler for userLoaded: UserManager.events.addUserLoaded. This will pass the new User with a new expire time

Redirect user to different route depending on registration process in Meteor using Iron Router

got quite a complicated problem.
In my application, there is a 5 step user registration/setup process.
Create Account
Choose an option
Choose more options
Create subscription with Stripe
Connect Stripe account so application has read-only access to user's Stripe data
After these steps, the user has access to the dashboard.
What I want to do is make sure that
If a user leaves/disconnects during a step when they log back in they will be redirected to the step they hadn't completed (currently my login submit handler redirects straight to dashboard, so I'm not sure how to handle this)
Once a user has completed all steps if they were to navigate to the route of a step they won't be able to, it will just forward to dashboard
I'd really appreciate any help on how to do this. So far what I've done is create a field in the database attached to the user document which records what step they have completed last, and if they have completed all it stored "dashboard".
I'm using Iron Router and I think the answer may lie in onBeforeAction hooks but I'm not 100% sure.
Any help appreciated!
I actually managed to work this out so thought I'd post for anyone who has a similar issue.
I extended the route controller for the routes I want to protect. The extended controller calls a meteor method that returns the value in the database that is stored when a user completes a registration step. I stored this value as a string which is the route the user should be redirected to.
So now, when a user signs up and completes step 2 then logs out a few things happen.
The app knows they have completed step 2 so stores the route for step 3 in a field on the user document (eg. Meteor.user().loginRedirectTo = '/subscribe')
User comes back and logs in, the loginhandler has a onBeforeAction that calls the meteor method which returns the value from the database to the router of where the user should go next.
Router sends users to that page.
Here's the code for it
Extended Controller:
StageController = RouteController.extend({
onBeforeAction: function() {
Meteor.call('getUserRedirect', function(error, route) {
if(error) {
console.log(error);
} else {
Router.go(route);
}
});
this.next();
}
});
Meteor method to return route user needs to go to:
Meteor.methods({
'getUserRedirect': function () {
if(!Meteor.userId()) {
throw new Meteor.Error(401, "You aren't even logged in, stop calling methods!");
}
var user = Meteor.user();
var redirectRoute = Meteor.user().loginRedirectTo;
return redirectRoute;
}
});
And finally assign that controller to each of the routes with this code:
Router.route('/dashboard', {
template: "dashboard",
name: "dashboard",
controller: "StageController"
});

where sfGuard checks out perms and credentials in order to implement Google's Oauth 2

I want to integrate Google's Oauth2 in my symfony-1.4 CRM. I have successfully implemented this, I have extended sfGuardAuth in my own module, and now mysfGuardAuth is being used for siging and signout. Is there where I handle Google's Oauth2 with 2 extra actions:
executeCkeckGoogleAccess();
executeOauth();
The problem is to checkout if Google's token is still a valid one, I have to redirect in each action of everymodule to the action checkGoogleAccess in mysfGuardAuth module.
What I want is to check this in an implicit way in the same place where symfony, or sfGuard or whatever checks for the right perms or credentials before executing or not executing the requested action.
I only want to write the code once.
Thank you.
After some research this is how sfGuard checks everything.
When you make a request to a module action, before the action is executed, a new sfContext is dispached.
The sfContext gets the user that extends sfGuardUser and has some methods that are executed. There is where perms, session status and everithing else is checked
The user must be configured in apps/yourApp/lib
By default is apps/yourApp/lib/myUser which extends sfGuardUser. The most apropiate way to achieve this is to create a new user class like: apps/yourApp/lib/yourAppUser which extends aswell sfGuardUser, and there extend the methods initialize and/or shutdown with the functionality you want.
By this way I have achieved to get Google's Oauth2 working in my app.
I hope this is usefull for more people.
UPDATE
All described above is true, but if you want to check something always before an action execution you must use filters instead of whats described before.
Filters are executed before each action, so there you can checkout whatever you need having access to the current context, and set up new attributes for the user. In my case I wanna check if the requested action needs a google token, if true, then Another filter will check if the user has alraedy a valid token, in that case, nothing happens, otherwise, the user is redirected to the module/action which handles google token requests.
Comunication between diferent filters, actions and requests are handled via user attributes.
the user is an object of the clas myOwnUser which extends sfGuardSecurityUser, there the function signOut is extended in order to delete all attributes saved in "myOwnNamespace"

How do you securely log out and clear all subscriptions?

I implemented my own login system, because I'm using a third party web service to authenticate users against an enterprise authentication system. As such, I built a form that calls a server method to make the web service call to the auth system, and if the credentials are valid, it sets a session variable with the user's id. This is how I change the template to show the main screen of the application and not the login screen. Works fine. And the logout button then just sets that userid session variable to false, effectively hiding the main application screen and showing the login form again.
<body>
{{#if loggedInUser}}
{{> navbar}}
{{> mainScreen}}
{{else}}
{{> customLogin}}
{{/if}}
</body>
Template.navbar.helpers({
loggedInUser: function () {
return Session.get('userName');
}
});
'click #logoutButton': function () {
Session.set("userName", false);
}
What I have discovered though, is that the local minimongo collections/subscriptions are still in the browser, and accessible in the console, after the user logs out.
I did some searching but didn't find concrete solutions as to how to properly clear out (or stop?) these subscriptions on the client. In fact, the top 3 hits on a search for "meteor publish subscribe " don't mention stopping or security upon logout.
One suggestion on SO was to save the subscription handle ... but I'm calling subscribe multiple times, so it seems I would have to store up an array depending on how many different subscribes the user triggered during their use of the application, and then go through them calling "stop" on each handle when logging out??
I'm hoping there's a simple way to stop all subscriptions... seems like a logical thing to do for security when a user clicks a logout button.
Thanks!
Could you not use .stop() function on the collection?
var subscription = Meteor.subscribe("info");
//on logout
subscription.stop();
According to the docs:
stop()
Cancel the subscription. This will typically result in the server directing the client to remove the subscription's data from the client's cache.
Updated: Maybe check out this package: Subs Manager. It appears they may be able to do what you want, specifically from their readme:
Clear Subscriptions
In somecases, we need to clear the all the subscriptions we cache. So, this is how we can do it.
var subs = new SubsManager();
// later in some other place
subs.clear();

Resources