Using multiple Roots with a Durandal application - single-page-application

Problem:
My application does not start; it just goes into an infinite loop.
Code:
My app.start:
app.start().then(function() {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
//configure routing
router.useConvention();
router.mapNav('home');
router.mapNav('intro');
router.mapNav('error');
router.mapRoute('set/:id', 'viewmodels/set', 'Set');
router.mapRoute('folder/:id', 'viewmodels/folder', 'Folder');
router.mapRoute('api', 'viewmodels/api', 'API Reference');
app.adaptToDevice();
app.setRoot('viewmodels/intro');
//logger.logError('No route found', route, 'main', true);
/*
router.handleInvalidRoute = function (route, params) {
//debugger;
//router.navigateTo('#/error');
};
*/
});
I set the root to intro, which contains a simple viewmodel that has a login function:
define(['durandal/app', 'durandal/plugins/router', 'services/dataservice'], function (app, router, dataservice) {
var introViewModel = function () {
var self = this;
self.router = router;
self.logIn = function () {
app.setRoot('viewmodels/shell');
};
self.activate = function () {
return router.activate('intro');
};
The goal here is to re-set the root of the application if the user is logged in. Any idea what I'm doing wrong?

Based on Rob's answer at the Google Groups page, here are some specifics that worked for me that may help others:
In main.js app.start().then(): set root to be your login page.
After login, configure your router and add routes to it. (Adding
routes must come before router activation.) This is an opportunity
to retrieve only those routes to which the user has access as part of
the login process.
Finally, set app root to be the shell, which then activates the
router with the default route you specify. Currently, I modify the
activate function in router to take an additional parameter of a
start url--that way, I can bypass the default page if need be.

Per Rob over on the Google Groups, I just plucked the router from my intro page and ran it in my shell.js, activating the router only once. This seems to tighten things up a bit and the app is working now:
https://groups.google.com/forum/#!topic/durandaljs/RdGpwIm1oOU

Based on your answer, the first part (setroot to login), I did this but the login page wasn´t loaded, no errors was showed also.
In main I simply put app.setRoot('viewmodels/login', 'entrance');
In login I simply have an activate function:
function activate() {
return true;
}
Is it correct?

Related

Vercel circular redirects when using Firebase authentication

I do have a super weird error coming up only when deploying the code to Vercel. It doesn't happen locally which makes it quite annoying to begin with.
I do have a staging and a production instance for my code. I want to protect the staging with a password which is not difficult since I implemented the authentication via Firebase. The only tricky part is that I don't use Firebase to keep track of the user but my server (basically setting a cookie). I should mention that I am using Sveltekit to put it all together.
In sveltekit you can use hooks, which can be seen as middlewares, to redirect a user to the sign-in page if the env variable for the environment is set to dev.
Another hook redirects a logged-in user, so if you are already logged in and try to go to auth/sign-in or auth/sign-up you'll get redirected to the home page.
Now the weird happens: I go on the deployed version of the site, and I get immediately redirected to the sign-in page, which is correct. I try to navigate to all the pages of the website, the redirect still works fine. I log in and upon success, I should be redirected to the homepage, which I do BUT the home page redirects me to the sign-in page as if I wasn't logged in and again the sign-in page redirects me to the home page as if I was, thus creating a loop.
I honestly don't know why this happens since it perfectly works locally, so my thoughts go to Vercel. I would exclude Firebase since I remembered to put the custom domain as an allowed domain in the settings.
To give a bitmore context, I structured the hooks responsible for the redirect in this way:
export const authSessionHandler: Handle = async ({ event, resolve }) => {
const cookie = event.locals.cookie;
const idToken = await getIdTokenFromSessionCookie(getCookieValue(cookie, 'session'));
const user = idToken
? {
uid: idToken?.sub,
email: idToken?.email
}
: null;
event.locals.idToken = idToken;
event.locals.user = user;
return resolve(event);
};
export const redirectLoggedInUserHandler: Handle = async ({ event, resolve }) => {
const { user } = event.locals;
const next = event.url.searchParams.get('next') || '/';
if (
user &&
(event.url.pathname.startsWith('/auth/sign-in') ||
event.url.pathname.startsWith('/auth/sign-up'))
) {
return new Response('Redirect', {
status: http_302.status,
headers: {
location: `${next}`
}
});
}
return resolve(event);
};
export const redirectToSignInForDevEnvironmentHandler: Handle = async ({ event, resolve }) => {
const { user } = event.locals;
const allowedEndpoints = ['/auth/sign-in', '/auth/session'];
if (!user && env === 'dev' && !allowedEndpoints.includes(event.url.pathname)) {
return new Response('Redirect', {
status: http_302.status,
headers: {
location: '/auth/sign-in'
}
});
}
return resolve(event);
};
The handlers are in that order, so the first one populates the user and the rest can check the rest.
In the code I am getting the user from event.locals which kind of decides the entire logic (as it should) and to me it's quite interesting and telling the fact that the sign-in page redirects me to home which mean the user is defined, but the home page redirects back as if the user was not defined. This made me think it is not a problem with the code but probably the provider(s) Vercel or Firebase.
It would be very helpful to know your thoughts about it.

Cloudflare Workers - changes are not visible on live (but are in preview)

Hello and thank you for your help.
Sadly support over at CF does not think they need to help me.
I am learning to use workers, and have written a simple HTML injector just to see it working on my site.
this is the full worker code i have:
async function handleRequest(req) {
const res = await fetch(req)
const contentType = res.headers.get("Content-Type")
console.log('contentType: ', contentType)
// If the response is HTML, it can be transformed with
// HTMLRewriter -- otherwise, it should pass through
if (contentType.startsWith("text/html")) {
return rewriter.transform(res)
} else {
return res
}
}
class UserElementHandler {
async element(element) {
element.before("<div class='contbox'><img src='https://coverme.co.il/wp-content/uploads/2020/01/covermeLOGO-01-1024x183.png' style='width:200px;margin:20px;'><h1>testing inserting</h1></div>", {html: true});
// fill in user info using response
}
}
const rewriter = new HTMLRewriter()
.on("h1", new UserElementHandler())
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
it just uses element.before to inject some HTML.
in the worker preview pane i can see it!
but on the live site = nothing.
this is the active URL: [https://coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/]
these are the 4 routes i have set up to try to catch this, with and without encoding the letters:
coverme.co.il/product/נר-בינוני-tuberosejasmine/
*.coverme.co.il/product/נר-בינוני-tuberosejasmine/*
https://coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/
*.coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/*
thanks in advance!
I believe the problem here is that you've configured your routes to match "נר-בינוני" unescaped, but the browser will actually percent-encode the URL before sending to the server, therefore the route matching actually operates on percent-escaped URLs. So the actual URL is https://coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/, and this does not match your route because %D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99 is not considered to be the same as נר-בינוני.
EDIT: Unfortunately, using percent-encoding in your route pattern won't fix the problem, due to a known bug. Unfortunately, it's just not possible to match non-ASCII characters in a Workers route today. We intend to fix this, but it's hard because some sites are accidentally dependent on the broken behavior, so the fix would break them.
What you can potentially do instead is match against coverme.co.il/product/*, and then, inside your worker, check if the path also has נר-בינוני-tuberosejasmine. If it does not, then your fetch event handler should simply return without calling event.respondWith(). This will trigger "default handling" of the request, meaning it will pass through and be sent to your origin server like normal. (Note that you will still be billed for a Workers request, though.)
So, something like this:
addEventListener("fetch", event => {
if (event.request.url.includes(
"coverme.co.il/product/נר-בינוני-tuberosejasmine/")) {
event.respondWith(handle(event.request));
} else {
return; // not a match, use default pass-through handling
}
})

Meteor: Using iron router and custom authentication issue

I might have this pretty close but I'm lacking the knowledge to fix this last issue.
I wanted to use a custom authentication system instead of using accounts-ui so I could track some additional details about each user.
Everything worked great until I get to the resetPassword part. If a user submits their email address in the forgotPassword form, the email is received. But when you click the reset password link in the email it does not display the resetPassword template.
This is on SO here:
Meteor account email verify fails two ways
And the iron-router github issue tracker here (which has the most fixes though is more focused on the enrollmentemail than resetPassword which I'm assuming should be very similar):
Iron-router swallows Accounts.sendEnrollmentEmail
If I understand correctly from the iron-router issue tracker above, iron-router doesn't (or didn't and maybe still doesn't) support hashbang urls like that being sent in the reset password email. A URL like:
http://localhost:3000/#/reset-password/T4rPxcVNWKwBONHSRajSk7dNZvM_YRxTLyzxZVv5SuU
Meteor was then updated so that meteor accounts-base strips out everything after the # and stores them in variables in the Accounts namespace.
While I think I understand all of that, now the question is why I can't get the suggestions in the issue tracker to work for my reset password code. I'm using everything that is in the custom auth system by Julien Le Coupanec and then I've done the following from the issue tracker:
router.js
Router.map(function() {
this.route('invList', {path: '/'});
this.route('resetPassword', {
controller: 'AccountController',
path: '/reset-password/:token',
action: 'resetPassword'
});
});
AccountController = RouteController.extend({
resetPassword: function () {
Accounts.resetPassword(this.params.token, function () {
Router.go('/reset-password');
});
}
});
overrideaccounts.js in /server
(function () {
"use strict";
Accounts.urls.resetPassword = function (token) {
return Meteor.absoluteUrl('reset-password/' + token);
};
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl('verify-email/' + token);
};
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('enroll-account/' + token);
};
})();
I'm wondering if the issues isn't related to either bad routing on my part (likely since I don't have my head wrapped around it well yet), if I put "server code" as is listed in the issue track in the right place, or if the session related code below is what is causing the resetPassword template to not display. Or something else that I'm missing of course.
main.js
//forgotPassword helper and event handler
Template.main.helpers({
showForgotPassword: function() {
return Session.get('showForgotPassword');
},
resetPassword: function(){
return Session.get('resetPassword');
}
});
After spending many hours on what I thought would be a really simple authentication system, I'm still at a loss. Appreciate any advice!
Don't struggle with hacking the hash and iron router, just back to Meteor original design flow.
When user click the verify link in email, it lead back to "/" (home), so just did this:
Template.home.created = function() {
if (Accounts._verifyEmailToken) {
Accounts.verifyEmail(Accounts._verifyEmailToken, function(err){
if (err != null) {
// handle the error
} else {
// do what you want, maybe redirec to some route show verify successful message
}
});
}
};
I did this and verify email right, same way worked for enroll, reset password...

$firebaseSimpleLogin and session without re-login

I am using $firebaseSimpleLogin to log into Firebase using email/password.
It is working rather well when I log in using email/password, I could see sessionkey being saved automatically as a cookie.
However, would like to remember the log in such that user only have to log in once.
So I included {rememberMe: true} during auth.
How do I check if the session is still alive at the beginning of the page being loaded?
From your question, I assume you're using Angular JS.
You can execute a run block on your main module, which is run everytime the page is loaded. I don't know much about Angularfire, this is the code I'm using on a hack day project to check auth and redirect to the login page if needed.
FirebaseRef is a wrapper that points to my Firebase instance.
This also makes sure that the currentUser object is available in all scopes.
var minib = angular.module('minib', ['ngRoute', 'firebase']);
minib.run(function($rootScope, $location, $firebaseSimpleLogin, firebaseRef) {
$rootScope.auth = $firebaseSimpleLogin(firebaseRef());
$rootScope.auth.$getCurrentUser().then(function(user) {
if (user) {
$rootScope.currentUser = user;
} else {
$location.path('/login');
}
});
});

How to protect a file directory and only allow authenticated users to access the files?

how do I restrict a folder, so only those who logged in into my Meteor app can download files?
I looked into multiple ways of doing this, but the main problem is that I can't access ( I get null.) with:
Meteor.user() or this.userId()
I tried:
__meteor_bootstrap__.app
.use(connect.query())
.use(function(req, res, next) {
Fiber(function () {
// USER HERE?
}).run();
});
or
__meteor_bootstrap__.app.stack.unshift({
route: "/protected/secret_document.doc", // only users can download this
handle: function(req, res) { Fiber(function() {
// CHECK USER HERE ?
// IF NOT LOGGED IN:
res.writeHead(403, {'Content-Type': 'text/html'});
var content = '<html><body>403 Forbidden</body></html>';
res.end(content, 'utf-8');
}).run() }
});
You could try storing the files in mongodb, which would mean that they would then be hooked into your collection system and be queryable on the client and server. Then, just publish the relevant data to the client for specific users, or use Meteor.methods to expose information that way.
Example:
Assuming files are stored in MongoDB, let's first publish them to the client:
Meteor.publish("files", function(folder) {
if (!this.userId) return;
// the userHasAccessToFolder method checks whether
// this user is allowed to see files in this folder
if (userHasAccessToFolder(this.userId, folder))
// if so, return the files for that folder
// (filter the results however you need to)
return Files.find({folder: folder});
});
Then on the client, we autosubscribe to the published channel so that whenever it changes, it gets refreshed:
Meteor.startup(function() {
Meteor.autosubscribe(function() {
// send the current folder to the server,
// which will return the files in the folder
// only if the current user is allowed to see it
Meteor.subscribe("files", Session.get("currentFolder"));
});
});
NB. I haven't tested above code so consider it pseudocode, but it should point you in the general direction for solving this problem. The hard part is storing the files in mongodb!
i'd be more concerned as to why Meteor.user() isn't working.
a few questions:
are you on meteor 0.5.0?
have you added accounts-base to your meteor project?
have you used one of meteor's login systems (accounts-password, accounts-facebook, etc)? (optional - accounts-ui for ease of use?)
have you still got autopublish on? or have you set up publishing / subscription properly?
Meteor.user() should be the current user, and Meteor.users should be a Meteor Collection of all previous logged in users.

Resources