auth0 / angular2-jwt

Helper library for handling JWTs in Angular apps
MIT License
2.63k stars 485 forks source link

Usage with universal #30

Closed thedark1337 closed 4 years ago

thedark1337 commented 8 years ago

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.

escardin commented 8 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.

chenkie commented 8 years ago

I think adding universal support is a good idea. I'll take a closer look at this for a future release.

coryshaw1 commented 8 years ago

+1

BhaskaranR commented 8 years ago

+1

jhades commented 8 years ago

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
neilhem commented 8 years ago

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?

dottodot commented 8 years ago

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.

escardin commented 7 years ago

@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.

dottodot commented 7 years ago

@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.

escardin commented 7 years ago

I honestly don't know. afaik Ionic abstracted away localstorage to solve that particular problem.

Koslun commented 7 years ago

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.

eduardmarcinov commented 6 years ago

It is possible to use @ngx-auth/core instead of this package for angular universal?

eduardmarcinov commented 6 years ago

ngx-auth/core

eduardmarcinov commented 6 years ago

https://www.npmjs.com/package/@ngx-auth/core

Is it possible to use instead of this package for angular universal app?

Koslun commented 6 years ago

@abdekalder It seems to have support for that, yes. Better to ask in the issues over there though: https://github.com/fulls1z3/ngx-auth

huan commented 6 years ago

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

patrickhousley commented 6 years ago

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)
alexnoise79 commented 6 years ago

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%

MakoPhil commented 6 years ago

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.

alexnoise79 commented 6 years ago

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

Eraph commented 6 years ago

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...)

vptill commented 6 years ago

Have you found the solution @patrickhousley ? I am struggling with it for hours, but can't make it work.

BenjaminDish commented 6 years ago

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 ?

alexnoise79 commented 6 years ago

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 ;)

BenjaminDish commented 6 years ago

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 ?

JunkyDeLuxe commented 5 years ago

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)

Tonksi commented 5 years ago

/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

Sanafan commented 5 years ago

@alexnoise79 you changed to sessionStorage inside your example but sessionStorage isn't persistent or what is the trick?

alexnoise79 commented 5 years ago

@Sanafan simply no trick at all, i just want the user to be logged out if i close the browser... your choice ;)

alexnoise79 commented 5 years ago

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 :)

alex-osman commented 5 years ago

@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?

marcus29200 commented 5 years ago

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)
eyalhakim commented 5 years ago

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

sumanbh commented 5 years ago

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

Joniras commented 5 years ago

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",       
beshad commented 5 years ago

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

stale[bot] commented 4 years ago

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! 🙇‍♂️