Closed thedark1337 closed 4 years ago
There are two calls to local storage that are likely breaking use with universal. One can be replaced by specifying a replacement getter function in the bootstrap of AuthConfig. The second won't get hit unless you call tokenNotExpired without passing a token.
I think adding universal support is a good idea. I'll take a closer look at this for a future release.
+1
+1
Its possible to override the token getter by passing {tokenGetter: yourFunction}
. But then it breaks due to a dependency with window (see here):
ORIGINAL STACKTRACE:
ReferenceError: window is not defined
As @escardin mentioned in case tokenNotExpired
called without passing token we get localStorage is not defined
, we need to generate jwt token on server and pass it when we call tokenNotExpired
function?
You can use DI swapping to get around this so client side you have
loggedIn() {
return tokenNotExpired();
}
and server side you have
loggedIn() { }
What I don't like about this though is when the app loads and they are logged in the app will show the not logged in state until the client side kicks in.
I've not used jwt before so I might be missing something, but I can't see that there is a server side equivalent for tokenNotExpired
that can used to check if the user is logged as it requires the token which is only available client side.
I might be wrong and I'm hoping I am as this is less than ideal.
@dottodot DI swapping is the right way to go for the most part. You don't need to remove the check in the server side version though. You want to do the check differently.
@escardin what is the server side check then if using local storage as I can't see how that's possible, using cookies is possible as the token can be accessed in res.cookies.
I honestly don't know. afaik Ionic abstracted away localstorage to solve that particular problem.
To solve both AoT & universal I did a local fork where I exported relevant services for AoT and wrapped localstorage access with a service which I DI swapped in universal with a service that just did not store anything on the server.
But I believe all you really need to do is to always properly check whether localstorage is available. Quick Stackoverflow search seems to recommend the modernizr way of checking:
Modernizr.addTest('localstorage', function() {
var mod = 'modernizr';
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
} catch(e) {
return false;
}
});
Think this might be a good thing to do anyway as I think localstorage is still unavailable in some private browsing modes of some browsers that are still quite common, at least according to caniuse.
It is possible to use @ngx-auth/core instead of this package for angular universal?
ngx-auth/core
https://www.npmjs.com/package/@ngx-auth/core
Is it possible to use instead of this package for angular universal app?
@abdekalder It seems to have support for that, yes. Better to ask in the issues over there though: https://github.com/fulls1z3/ngx-auth
Today I found that when I run a script that import the @auth0/angular-jwt@2.0.0
under Node.JS environment, it throws a error message: ReferenceError: window is not defined
+node smoke-testing.js
/tmp/npm-pack-testing.5971/node_modules/@auth0/angular-jwt/bundles/core.umd.js:1
(function (exports, require, module, __filename, __dirname) { !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("@angular/core"),require("@angular/common/http"),require("rxjs/operators"),require("rxjs/internal/observable/from")):"function"==typeof define&&define.amd?define(["@angular/core","@angular/common/http","rxjs/operators","rxjs/internal/observable/from"],e):"object"==typeof exports?exports["angular-jwt"]=e(require("@angular/core"),require("@angular/common/http"),require("rxjs/operators"),require("rxjs/internal/observable/from")):t["angular-jwt"]=e(t["@angular/core"],t["@angular/common/http"],t["rxjs/operators"],t["rxjs/internal/observable/from"])}(window,function(t,e,r,n){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Ob
ReferenceError: window is not defined
at Object.<anonymous> (/tmp/npm-pack-testing.5971/node_modules/@auth0/angular-jwt/bundles/core.umd.js:1:704)
at Module._compile (internal/modules/cjs/loader.js:702:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
at Module.load (internal/modules/cjs/loader.js:612:32)
at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
at Function.Module._load (internal/modules/cjs/loader.js:543:3)
at Module.require (internal/modules/cjs/loader.js:650:17)
at require (internal/modules/cjs/helpers.js:20:18)
at /tmp/npm-pack-testing.5971/node_modules/auth-angular/bundles/auth-angular.umd.js:2:111
at Object.<anonymous> (/tmp/npm-pack-testing.5971/node_modules/auth-angular/bundles/auth-angular.umd.js:5:2)
See the details at: https://travis-ci.com/Chatie/db/jobs/126218183#L861
Using the angular cli to perform the ssr bundle, I keep getting the below error:
/Users/phousley/Documents/projects/buffalo-web/node_modules/@auth0/angular-jwt/src/jwthelper.service.js:2
import { Injectable, Inject } from '@angular/core';
^^^^^^
SyntaxError: Unexpected token import
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
at Module._compile (module.js:616:28)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at Object.@auth0/angular-jwt/src/jwthelper.service (/Users/phousley/Documents/projects/buffalo-web/dist/@buffalo/server/main.js:15266:18)
Hello,
i'll give you my workaround (hope it helps)
import { NgModule, PLATFORM_ID } from '@angular/core';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { JWT_OPTIONS, JwtModule } from '@auth0/angular-jwt';
export function jwtOptionsFactory(platformId) {
return {
tokenGetter: () => {
let token = null;
if (isPlatformBrowser(platformId)) {
token = sessionStorage.getItem('token');
}
return token;
},
whitelistedDomains: ['localhost:3000']
};
}
@NgModule({
imports: [
CommonModule,
JwtModule.forRoot({
jwtOptionsProvider: {
provide: JWT_OPTIONS,
useFactory: jwtOptionsFactory,
deps: [PLATFORM_ID]
}
})
]
})
export class AuthModule {
constructor() {
}
}
build: 100% build-ssr: 100%
Thanks for sharing that, @alexnoise79. Has anyone seen this working? Because even with that change I still get window is not defined
after rebuilding SSR.
It seems that as long as JwtHelperService is being referenced in the project, that error will appear, presumably as part of some initialisation. It has crossed my mind that I could separate functionality between Server/Browser modules, providing the real service for browser only while providing a mock for the server, but this seems like an overly complicated workaround. Ideally I would like to not have to worry about whether window
exists or not and just get on with it!
--Edit So I just tried to use the Service standalone, that is without using DI, no import in the module definition. Still getting the error.
Hi @MakoPhil, are you sure that "window is not defined" is coming from angular2-jwt package?
Cause i'm not experiencing that in v2.0.0
I'm afraid so, @alexnoise79. As soon as I import it and instantiate a new instance of JwtHelperService
, I get the error when trying to load any page. This was using v2.0.0, Angular 6.
I have resorted to writing my own JWT payload reader, and built an HTTP interceptor to do what I needed it to do, but I think I would prefer to use a battle-tested solution so I'm keen to follow this for a future fix.
(Oops, different account, but still me...)
Have you found the solution @patrickhousley ? I am struggling with it for hours, but can't make it work.
Hello @alexnoise79 . Is your workaround working with 2.0.0 ?
I have a TS error compiling your code like this :
ERROR in src/app/auth.module.ts(24,17): error TS2345: Argument of type '{ config: { provide: InjectionToken<{}>; useFactory: (platformId: any) => { tokenGetter: () => an...' is not assignable to parameter of type 'JwtModuleOptions'.
Types of property 'config' are incompatible.
Type '{ provide: InjectionToken<{}>; useFactory: (platformId: any) => { tokenGetter: () => any; whiteli...' is not assignable to type '{ tokenGetter?: () => string | Promise<string>; headerName?: string; authScheme?: string; whiteli...'.
Object literal may only specify known properties, and 'provide' does not exist in type '{ tokenGetter?: () => string | Promise<string>; headerName?: string; authScheme?: string; whiteli...'.
I can't figure out how to translate your code to the "new way". Can you ?
HI @BenjaminDish,
my workaround is for version 2.0.0.
it seems that you forgot something in the provide section, please be sure to follow the code i wrote.
Or be sure to respect the type and values of the object when you setup the jwtOptionsProvider.
Hope it helps ;)
Thanks @alexnoise79
I fixed the TS problem thanks to your advice but I still have the "window is not defined" error.
I copied your code in an auth.module.ts and just changed the tokenGetter method to match my way :
import { NgModule, PLATFORM_ID } from '@angular/core';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { JWT_OPTIONS, JwtModule } from '@auth0/angular-jwt';
export function jwtOptionsFactory(platformId) {
return {
tokenGetter: () => {
let token = null;
if (isPlatformBrowser(platformId)) {
token = localStorage.getItem('id_token');
}
return token;
},
whitelistedDomains: ['localhost','my.domain.com']
};
}
@NgModule({
imports: [
CommonModule,
JwtModule.forRoot({
jwtOptionsProvider: {
provide: JWT_OPTIONS,
useFactory: jwtOptionsFactory,
deps: [PLATFORM_ID]
}
})
]
})
export class AuthModule {
constructor() {
}
}
Then I have imported this AuthModule in the AppModule.
When running with SSR :
{my_path}\node_modules\@auth0\angular-jwt\bundles\core.umd.js:1
ReferenceError: window is not defined
at Object.<anonymous> ({my_path}\node_modules\@auth0\angular-jwt\bundles\core.umd.js:1:704) at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at Object.@auth0/angular-jwt ({my_path}\dist\server\main.js:3734:18)
at __webpack_require__ ({my_path}\dist\server\main.js:23:30)
Did I miss something ?
Guys, the error window is pretty standard one, with an express server, you don't have the global variable window, but you can fix it:
In your server.ts file, at the root of your project (see angular-universal-starter from main documentation of angular.io)
U can add thoses lines
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join(__dirname, '.', 'browser', 'index.html')).toString();
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
BUT, anyway, even with the solution of create a module for angular-jwt with the solution of @alexnoise79, I have got an other error
After executing node dist/server (the build is ok by the way)
/Users/simon/Desktop/universal-starter/node_modules/@auth0/angular-jwt/src/jwtoptions.token.js:1 (function (exports, require, module, filename, dirname) { import { InjectionToken } from '@angular/core'; ^^^^^^
SyntaxError: Unexpected token import at createScript (vm.js:80:10) at Object.runInThisContext (vm.js:139:10) at Module._compile (module.js:616:28) at Object.Module._extensions..js (module.js:663:10) at Module.load (module.js:565:32) at tryModuleLoad (module.js:505:12) at Function.Module._load (module.js:497:3) at Module.require (module.js:596:17) at require (internal/module.js:11:18) at Object.@auth0/angular-jwt/src/jwtoptions.token (/Users/simon/Desktop/universal-starter/dist/server/main.js:593:18)
/Users/simon/Desktop/universal-starter/node_modules/@auth0/angular-jwt/src/jwtoptions.token.js:1 (function (exports, require, module, filename, dirname) { import { InjectionToken } from '@angular/core'; ^^^^^^
I ended up just taking a copy of the repo and transpiling it myself, that's working fine (but inconvenient for npm/node_modules). For reference I am using angular7/universal, domino, nx
@alexnoise79 you changed to sessionStorage inside your example but sessionStorage isn't persistent or what is the trick?
@Sanafan simply no trick at all, i just want the user to be logged out if i close the browser... your choice ;)
Just to update and clarify, the workaround work even with 2.1.0 for me, i got no issues or "window is not defined"
there are 3 occourrences of window in the core.umd.js file, but i think in my case i'm not invoking any of those functions.
the only extra function i'm using (apart from the one listed above) is "isTokenExpired" from the JwtHelperService.
Again hope it helps :)
@Tonksi @JunkyDeLuxe @vptill @patrickhousley Is there a solution to this error?
/Users/alex/Documents/repo/customer/node_modules/@auth0/angular-jwt/src/jwthelper.service.js:2
import { Injectable, Inject } from '@angular/core';
^
SyntaxError: Unexpected token {
at new Script (vm.js:74:7)
at createScript (vm.js:246:10)
at Object.runInThisContext (vm.js:298:10)
It looks like a compile/tsconfig issue. Any solution?
I attempted the recommended solution as well, but now it is failing in a different spot.
/.../node_modules/@auth0/angular-jwt/src/jwtoptions.token.js:1
(function (exports, require, module, __filename, __dirname) { import { InjectionToken } from '@angular/core';
SyntaxError: Unexpected token {
at new Script (vm.js:85:7)
at createScript (vm.js:266:10)
at Object.runInThisContext (vm.js:314:10)
at Module._compile (internal/modules/cjs/loader.js:698:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10)
at Module.load (internal/modules/cjs/loader.js:630:32)
at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
at Function.Module._load (internal/modules/cjs/loader.js:562:3)
at Module.require (internal/modules/cjs/loader.js:667:17)
at require (internal/modules/cjs/helpers.js:20:18)
I attempted the recommended solution as well, but now it is failing in a different spot.
/.../node_modules/@auth0/angular-jwt/src/jwtoptions.token.js:1 (function (exports, require, module, __filename, __dirname) { import { InjectionToken } from '@angular/core'; SyntaxError: Unexpected token { at new Script (vm.js:85:7) at createScript (vm.js:266:10) at Object.runInThisContext (vm.js:314:10) at Module._compile (internal/modules/cjs/loader.js:698:28) at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10) at Module.load (internal/modules/cjs/loader.js:630:32) at tryModuleLoad (internal/modules/cjs/loader.js:570:12) at Function.Module._load (internal/modules/cjs/loader.js:562:3) at Module.require (internal/modules/cjs/loader.js:667:17) at require (internal/modules/cjs/helpers.js:20:18)
Same issue for me
Ran into the same issue. For now, I just forked the repo, and ended up using just ngc
to transpile the code instead of webpack. See here - angular-jwt-universal
What helped me was following (im sorry i cannot really explain it, it just works for me):
babel node_modules/@auth0/angular-jwt -d node_modules/@auth0/angular-jwt --presets @babel/preset-env
which appearently compiles it into somehting more usable for universal. (i added it into the postinstall script in package.json)
I also have installed following dependencies:
"@babel/cli": "^7.5.0",
"@babel/core": "^7.5.0",
"@babel/preset-env": "^7.5.0",
What helped me was following (im sorry i cannot really explain it, it just works for me):
babel node_modules/@auth0/angular-jwt -d node_modules/@auth0/angular-jwt --presets @babel/preset-env
which appearently compiles it into somehting more usable for universal. (i added it into the postinstall script in package.json)
I also have installed following dependencies:
"@babel/cli": "^7.5.0", "@babel/core": "^7.5.0", "@babel/preset-env": "^7.5.0",
this answer solved my problem too
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇♂️
Hi, can this module be used with angular2-universal-preview ? https://github.com/angular/universal/
I tried to set it up but it doesn't like the routing functions such as loggedIn()
I have a server and a client.js that bootstrap the same app.js with routes that use the loggedIn() but the server throws an error that it can't find loggedIn() under the context.