Ember - get target url of transition - javascript

I am creating an error hook in my Ember.js app to redirect you to the auth service if you are not allowed to view certain content (in other words, if the server returns a 401).
It looks like this:
Ember.Route = Ember.Route.extend({
error: function(error, transition){
if (error.status === 401) {
window.location.replace("https://auth.censored.co.za");
}
}
Our auth api works as follows: If you send it a parameter called target (which is a url), it will redirect you back to that target url after you've logged in.
So I want to somehow get the URL of the route the Ember app was trying to transition to.
Then my code will end up something like this
Ember.Route = Ember.Route.extend({
error: function(error, transition){
if (error.status === 401) {
var target = // Your answer here
window.location.replace("https://auth.censored.co.za?target=" + encodeURIComponent(target));
}
}

I came across a need for this too, and resorted to using some internal APIs. In my case, I wanted to reload the whole app so that if you're switching users there's not data left over from the other user. When I reload the app, I want to put the user at the URL they tried to transition to, but for which they had insufficient privileges. After they authenticate (and thus have the bearer token in localstorage) I wanted to use window.location.replace(url) to get a clean copy of the whole app with the user at the URL implied by the Ember Transition object. But the question was, how do I go from a Transition object to a URL? Here is my solution, which uses the generate method which is a private API of the router:
let paramsCount = 0;
let params = {};
let args = [transition.targetName];
// Iterate over route segments
for (let key1 in transition.params) {
if (transition.params.hasOwnProperty(key1)) {
let segment = transition.params[key1];
// Iterate over the params for the route segment.
for (let key2 in segment) {
if (segment.hasOwnProperty(key2)) {
if (segment[key2] != null) {
params[key2] = segment[key2];
paramsCount++;
}
}
}
}
}
if (paramsCount > 0) {
args.push(params);
}
let url = router.generate.apply(router, args);
You'll need to get the router somehow either with a container lookup or some other means. I got it by injecting the -routing service which is documented as an API that might be publically exposed in the future (used by link-to), and which happens to have the router as a property.
While messy, perhaps you might find this helpful.

I was able to use transition.intent.url to accomplish exactly this. I'm not sure if this is private or not -- relevant discussion: https://discuss.emberjs.com/t/getting-url-from-ember-router-transition-for-sso-login/7079/2.

After spending several hours searching for an answer to this question and using the Chrome debugger to try and reverse engineer the Ember 2.5 code, my conclusion is that what you are looking for is not possible at the present time.
For people who don't understand why someone wants to do this, the case arises when authentication (e.g the login page) is separated from the application. This is necessary if there is a requirement not to deliver any content (including the application itself) to the user if the user is not authenticated. In other words, the login page cannot be part of the application because the user is not allowed to access the application before logging in.
PS: I realize this is not a solution to the user's question and probably more suitable as a comment. However, I can't post comments.

Kevins answer is the most correct one, I came to a similar solution. Basically I found how the link-to component was populating the href attribute and used the same sort of code.
In your object inject -routing. I did so with:
'routing': Ember.inject.service('-routing'),
Then the code to generate the URL from the transition is as follows...
let routing = this.get('routing');
let params = Object.values(transition.params).filter(param => {
return Object.values(param).length;
});
let url = routing.generateURL(transition.targetName, params, transition.queryParams);

Related

Passing sessionstorage item into a link JavaScript or HTML

I am trying to create a single sign in, and far as I can think this would probably be the best.
My backend receives the request, checks for email, creates a JWToken and adds it to a link sent per email to the user.
Something along this line
function loadWindow() {
var setsession = window.sessionStorage.setItem("JWT", 'tokentoken');
window.open('http://localhost:8080/html/reset-password.html')
}
there must be some way, but no matter how I do this, the page will not load with a sessionstorage set.
This seems to have actually fixed it!
It needed a const definition..
Just sticking it onto the end did not work, but this seems to:
function loadWindow() {
const myWindow = window.open('http://localhost:8080/html/reset-password.html');
myWindow.localStorage.setItem('JWT', 'cat');
}
Of course ideally would be directly sessionStorage, but that is not an option it seems.
I will probably have to write a script that removes it from local and rewrites it to sessionstorage. Not sure if all this is best practice security wise though..
EDIT:
come to think of it, this only works because I am running it from tab to tab, likely that when just opening a link from an email this won't work.
EDIT2:
I also appreached this differently,
By passing the token to the url.com/?token=randomtokennumbersletters
And then creating a js function loadFunction that runs upon loading the page
function loadFunction() {
if (sessionStorage.getItem('JWT') === null) {
let params = (new URL(document.location)).searchParams;
let token = params.get("token");
sessionStorage.setItem('JWT', token) // slaat de token van de link op in localstorage
}
}

Auth0 unable to get ID token / user metadata

I am currently working on adding Auth0 to a Vue.js/Node.js application and so far I have figured out how to allow users to register and log in (to /callback) and that seems to be working fine. However, I have manually added (will be automatic later on) some data to the user metadata section. I have the below code as a rule that is turned on. I can’t seem to get access to the data on the Vue.js end of things. What I’d like is to be able to get the user data and user metadata so I can store it in my front end.
Rule code
function (user, context, callback) {
const namespace = 'account_signup_type/';
const namespace2 = 'account_type';
context.idToken[namespace + 'is_new'] = (context.stats.loginsCount === 1);
context.idToken[namespace2] = user.account_type;
context.idToken.user = user;
callback(null, user, context);
}
Code I am trying in my Vue.js front end
getIdTokenClaims(o) {
return this.auth0Client.getIdTokenClaims(o);
}
Currently, this returns undefined
I ended up figuring it out, there was no namespace being created in the id_token which resulted in it not properly passing the data through to the Vue .js app. I added a namespace using a web address format with the domain extension cut off and it now works.

Cannot initialise store from localStorage on App initialization

I'm struggling to initialize my Vuex store with the account details of the logged in user from localStorage.
I've been through as many examples as I can of Auth using Nuxt, and none of them demonstrate how on the client side to pull an authToken from localStorage to re-use with subsequent API requests when the user refreshes the page or navigates to the App
Disclaimer: I'm not using Nuxt in SSR (this might affect your answer).
What is annoying is that I can actually load from localStorage and initialize my state but then it gets overwritten. I'll show you what I mean with this small code example:
buildInitialState () {
if (!process.server) {
// Query the localStorage and return accessToken, refreshToken and account details
return {accessToken: <valid-string>, refreshToken: <valid-string>, account: <valid-json-blob>}
} else {
// Return "empty map", we use the string "INVALID" for demonstration purposes.
return {accessToken: "INVALID", refreshToken: "INVALID", account: "INVALID"}
}
}
export const state = () => buildInitialState()
If I put a breakpoint on buildInitialState I can see that I correctly initialize the state with the value of localStorage, i.e. I get the accessToken and refreshToken, etc.. back.
All seems well.
But then .....in another part of the program I'm using Axois to send requests, and I use an axios interceptor to decorate the request with the accessToken. To do this I have to stick it into a plugin to get access to the store.
Something like so:
export default ({ app, store }) => {
axios.interceptors.request.use((config) => {
const accessToken = _.get(store.state, ['account', 'accessToken'])
if (accessToken) {
config.headers.common['x-auth-token'] = accessToken
}
return config
}, (error) => Promise.reject(error))
}
Here the store is closed over in the arrow function supplied to axios so when I go to send the request it sees if there is a valid accessToken, and if so then use it.
BUT, and here's the kicker, when a request is made, I look at the store.state.account.accessToken and low and behold its been reinitialized back to the value of "INVALID".
What? Que?
It's almost like the store was reinitialized behind the scenes? Or somehow the state in the plugin is "server side state"?? because if I try and log buildInitialState I don't get any messages indicating that the path that produced a map with INVALID is being run.
Basically, I don't thoroughly understand the initialization pathway Nuxt is taking here at all.
If any Nuxt masters could help me out understand this a bit more that would be great, it's turning into a bit of a show stopper for me.
Essentially! All I want to be able to do is save the user so that when they refresh their page we can keep on running without forcing them to re-login again....I thought that would be simple.
Thanks and regards, Jason.
I've solved this with a bit of experimentation and comments from other posters around what is called SSR and SPA.
Firstly, this https://github.com/nuxt/nuxt.js/issues/1500 thread really helped me and the final comment from #jsonberry steered my mind in the right direction, away from fetch and asyncData.
I finally had a bit more of an understanding of how NUXT.js was separating SSR and SPA calls.
I then tried #robyedlin suggestion of putting localStorage initialization in the created() method for my main layout/default.vue page.
While I made progress with that suggestion it turns out created() is also called SSR and I was still trying to initialize my store from credentials that weren't accessible.
Finally, moving the initialization to mounted() did the trick!
So in summary:
My account store is left alone, I don't try and initialize it when it is created (it's just overwritten at some point when the SSR stuff runs)
On mounted() in layout/defualt.vue I read from localStorage and initialize the account store so I can start making API requests with the appropriate accessToken.
That seems to have done the trick.

How to skip or jump middleware? (node)

Given two middleware, how can you add something that would get hit first, then determine which of the middleware to pass the request onto. The request can only be dropped or forwarded to one of the middleware, never both.
app.use( function *(next) {
if (isAuthenticated)
*private middleware*
else
*use public middleware*
});
More Specifically:
I have a homepage / that should have a variable response depending on if the user is logged in or not (using token-based auth here). For simplicity, if the request user does not have a token, then public.html is the response while private.html for request bearing a valid token.
Now this would be easy enough to just implement within one function, but I'd like to have separated routers, figure that would keep the code more manageable.
So I need to somehow be able to choose which middleware router the request goes to right? No idea how to do that.
var publicR = new router();
publicR.get('/', function *(next) {
....public.html....
});
var privateR = new router();
privateR.get('/', function *(next) {
....private.html....
});
app.use(function(){
if(isAuthenticated)
...use privateR.routes();
else
...use publicR.routes();
});
First off, it's unusual and really not a good idea to present entirely different content for a given URL when logged in or not logged in. So, trying to have two different routers that all serve the same routes, but one is for a logged in user and the other for a not-logged in user is probably just not a good design idea.
The more usual case is to have a portion of a page that might be different when logged in. In that case, you have a single route creating the page that handles doing something slightly different with the content when logged in or not.
If you really want to have completely different content and behavior when logged in, then you should probably redirect to a different URL when logged in. In that case, you can just use an entirely different router for the "logged in" URLs. This will also work better for search engines since a given URL will report consistent content and not be different depending upon the state of the user. This also makes the use of Routers in Express really easy. Your "logged in" router serves the logged in URLs. You can have middleware that checks a logged-in URL to see if you are actually logged in and, if not, redirects back to the non-logged in page and vice versa.
In case anyone else runs into this issue, this is working for me:
var publicR = new router();
publicR.get('/', function *(next) {
....public.html....
});
var privateR = new router();
privateR.get('/', function *(next) {
....private.html....
});
app.use(mySwitcher);
function *mySwitcher(next){
if(isAuthenticated)
yield privateR.routes().call(this,next);
else
yield publicR.routes().call(this,next);
}

How to get Nice-Looking URLS - Node.JS

I have been looking around the web for a way to get the URL that is like:
example.com/games/game1 instead of example.com/games?id=game1
I have looked around the Node.JS website but I couldn't find anything that seemed to apply to my situation.
Any help is very appreciated. I have found an answer that did this using a .HTACCESS file, but I couldn't find a node.js alternative. The question/answer that I found was, creating nice looking URLs
Any help is very appreciated.
This URL example.com/games?id=game1 is passing the id as a GET parameter. To replace it with example.com/games/game1, you just have to come with a strategy on how to pass this id. This strategy is usually referred to node.js as routes, and, they are plenty of options on how to achieve your goal:
If you are using Express framework, you have built in ability to do stuff like this (based off TJ Holowaychuk's route separation examples):
app.get('/games/:id', games.view);
Then, in your game.js file:
exports.view = function(req, res){
console.log(req.params.id); //gives you game1
//...
};
- Another way to do it is to use something specific for routing (instead of a whole framework). Director comes to mind.
var viewGame = function(gameId) { console.log(gameId); };
var routes = {
'/games/:gameId': viewGame
};
You can list to all requests, then parse request.url to decide which page to render or whether to return a 404 / 302 or whatever you want to do. This is just a small example. You probably want to separate your routing from your logic:
var http = require("http");
http.createServer(function(request, response) {
var parts = request.url.split('/');
if(parts[0] === 'games'){
var id = parts[1];
// Check if valid id
// And render the correct page
}
}).listen(80);

Categories

Resources