Auth0 Error Cannot Read Property Crypto of Undefined

Today we'll have a look at how easy information technology is to integrate JSON web token (JWT) authentication into your Athwart vi (or later) single-page application (SPA). Let's start with a bit of background.

What Are JSON Spider web Tokens, and Why Use Them?

The easiest and most concise answer here is that they are convenient, compact, and secure. Let's wait at those claims in detail:

  1. Convenient: Using a JWT for authentication to the dorsum cease once logged in requires setting one HTTP header, a task which tin exist hands automated through a role or subclassing, as nosotros'll meet later.
  2. Compact: A token is simply a base64-encoded string, containing a few header fields, and a payload if required. The full JWT is usually less than 200 bytes, even if signed.
  3. Secure: While not required, a dandy security characteristic of JWT is that tokens can be signed using either RSA public/individual primal-pair encryption or HMAC encryption using a shared secret. This ensures the origin and validity of a token.

What this all boils downward to is that y'all take a secure and efficient way to authenticate users, so verify calls to your API endpoints without having to parse any data structures nor implement your own encryption.

Awarding Theory

Typical data flow for JWT authentication and usage between front-end and back-end systems

Then, with a bit of groundwork, we can now dive into how this would work in an actual awarding. For this case, I am going to assume we have a Node.js server hosting our API, and we are developing an SPA todo listing using Angular 6. Let's also work with this API structure:

  • /authPOST (mail service username and password to authenticate and receive back a JWT)
  • /todosGet (return a list of todo list items for the user)
  • /todos/{id}GET (return a specific todo list item)
  • /usersGet (returns a list of users)

Nosotros'll go through the creation of this simple application shortly, but for at present, let's concentrate on the interaction in theory. We accept a simple login page, where the user can enter their username and password. When the form is submitted, it sends that data to the /auth endpoint. The Node server can so authenticate the user in whatsoever fashion is appropriate (database lookup, querying another web service, etc.) just ultimately the endpoint needs to return a JWT.

The JWT for this example will incorporate a few reserved claims, and some individual claims. Reserved claims are just JWT-recommended fundamental-value pairs commonly used for hallmark, whereas private claims are cardinal-value pairs applicable but to our app:

Reserved Claims

  • iss: Issuer of this token. Typically the FQDN of the server, but tin exist anything as long equally the customer awarding knows to expect it.
  • exp: Death date and time of this token. This is in seconds since midnight 01 Jan 1970 GMT (Unix time).
  • nbf: Non valid before timestamp. Not used often, but gives a lower bound for the validity window. Aforementioned format as exp.

Private Claims

  • uid: User ID of the logged-in user.
  • office: Role assigned to the logged-in user.

Our data will be base64-encoded and signed using HMAC with the shared key todo-app-super-shared-secret. Below is an example of the what the JWT looks similar:

          eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b2RvYXBpIiwibmJmIjoxNDk4MTE3NjQyLCJleHAiOjE0OTgxMjEyNDIsInVpZCI6MSwicm9sZSI6ImFkbWluIn0.ZDz_1vcIlnZz64nSM28yA1s-4c_iw3Z2ZtP-SgcYRPQ                  

This cord is all nosotros need to brand sure that we accept a valid login, to know which user is connected, and to know even what office(southward) the user has.

Most libraries and applications proceed to store this JWT in localStorage or sessionStorage for easy retrieval, but this is just common do. What you do with the token is up to you, equally long as y'all can provide it for futurity API calls.

At present, whenever the SPA wants to brand a telephone call to any of the protected API endpoints, information technology simply needs to transport along the token in the Authorisation HTTP header.

          Authorization: Bearer {JWT Token}                  

Note: In one case again, this is simply common practice. JWT does not prescribe any item method for sending itself to the server. You could as well append it to the URL, or ship it in a cookie.

Once the server receives the JWT, information technology can decode it, ensure consistency using the HMAC shared secret, and bank check expiry using the exp and nbf fields. It could also use the iss field to ensure it was the original issuing party of this JWT.

Once the server is satisfied with the validity of the token, the information stored within the JWT can exist used. For example, the uid nosotros included gives us the ID of the user making the request. For this particular case, we besides included the role field, which lets us make decisions about whether the user should exist able to access a particular endpoint or not. (Whether you trust this data, or rather want to do a database lookup depends on the level of security required.)

          function getTodos(jwtString) {   var token = JWTDecode(jwtstring);   if( Date.now() < token.nbf*1000) {     throw new Error('Token not yet valid');   }   if( Date.now() > token.exp*thou) {     throw new Mistake('Token has expired');   }   if( token.iss != 'todoapi') {     throw new Error('Token not issued here');   }    var userID = token.uid;   var todos = loadUserTodosFromDB(userID);    return JSON.stringify(todos); }                  

Allow's Build a Uncomplicated Todo App

To follow along, yous volition demand to have a recent version of Node.js (half dozen.x or afterward), npm (iii.x or later), and athwart-cli installed. If you need to install Node.js, which includes npm, please follow the instructions here. Later angular-cli can be installed by using npm (or yarn, if you've installed it):

          # installation using npm npm install -grand @angular/cli  # installation using yarn yarn global add together @angular/cli                  

I won't go into detail on the Angular half-dozen boilerplate we'll use hither, but for the side by side step, I have created a Github repository to hold a modest todo application to illustrate the simplicity of calculation JWT authentication to your app. Simply clone it using the post-obit:

          git clone https://github.com/sschocke/angular-jwt-todo.git cd angular-jwt-todo git checkout pre-jwt                  

The git checkout pre-jwt command switches to a named release where JWT has not been implemented.

There should be two folders within called server and client. The server is a Node API server that volition host our basic API. The customer is our Angular 6 app.

The Node API Server

To get started, install the dependencies and start the API server.

          cd server  # installation using npm npm install  # or installation using yarn yarn  node app.js                  

Y'all should exist able to follow these links and get a JSON representation of the information. Just for at present, until we have authentication, nosotros accept hardcoded the /todos endpoint to return the tasks for userID=one:

  • http://localhost:4000: Test page to see if Node server is running
  • http://localhost:4000/api/users: Return list of users on the system
  • http://localhost:4000/api/todos: Return list of tasks for userID=1

The Angular App

To get started with the customer app, nosotros too demand to install the dependencies and start the dev server.

          cd client  # using npm npm install npm start  # using yarn yarn yarn start                  

Note: Depending on your line speed, information technology tin accept a while to download all the dependencies.

If all is going well, y'all should now see something similar this when navigating to http://localhost:4200:

The non-JWT-enabled version of our Angular Todo List app.

Adding Authentication via JWT

To add together support for JWT hallmark, nosotros'll make apply of some standard libraries available that make it simpler. You can, of course, forego these conveniences and implement everything yourself, but that is beyond our scope hither.

First, let's install a library on the client side. Information technology's developed and maintained by Auth0, which is a library allowing yous to add cloud-based authentication to a website. Utilizing the library itself does non require that you apply their services.

          cd customer  # installation using npm npm install @auth0/angular-jwt  # installation using yarn yarn add @auth0/angular-jwt                  

We'll go to the code in a second, just while we are at information technology, let's go the server side ready equally well. We'll apply the trunk-parser, jsonwebtoken, and express-jwt libraries to brand Node sympathize JSON Mail bodies and JWTs.

          cd server  # installation using npm npm install body-parser jsonwebtoken express-jwt  # installation using yarn yarn add together body-parser jsonwebtoken express-jwt                  

API Endpoint for Authentication

Kickoff, we need a way to authenticate users before giving them a token. For our simple demo, we are going to but set up a fixed hallmark endpoint with a hard-coded username and password. This can be as simple or every bit complex equally your application requires. The important thing is to send dorsum a JWT.

In server/app.js add together an entry beneath the other require lines as follows:

          const bodyParser = crave('body-parser'); const jwt = crave('jsonwebtoken'); const expressJwt = require('limited-jwt');                  

As well as the following:

          app.use(bodyParser.json());  app.post('/api/auth', function(req, res) {   const body = req.body;    const user = USERS.find(user => user.username == torso.username);   if(!user || trunk.password != 'todo') return res.sendStatus(401);      var token = jwt.sign({userID: user.id}, 'todo-app-super-shared-surreptitious', {expiresIn: '2h'});   res.send({token}); });                  

This is generally basic JavaScript code. We get the JSON body that was passed to the /auth endpoint, find a user matching that username, cheque that nosotros have a user and the countersign matches, and render a 401 Unauthorized HTTP error if not.

The important function is the token generation, and we'll break that downwards past its three parameters. The syntax for sign is as follows: jwt.sign(payload, secretOrPrivateKey, [options, callback]), where:

  • payload is an object literal of primal-value pairs that you would like to encode within your token. This information can then exist decoded from the token by everyone that has the decryption key. In our example, we encode the user.id so that when we receive the token again on the back end for authentication, we know which user we are dealing with.
  • secretOrPrivateKey is either an HMAC encryption shared secret key—this is what nosotros've used in our app, for simplicity—or an RSA/ECDSA encryption private cardinal.
  • options represents a diversity of options that can be passed to the encoder in the course of key-value pairs. Typically nosotros at to the lowest degree specify expiresIn (becomes exp reserved claim) and issuer (iss reserved merits) so that a token isn't valid forever, and the server can check that information technology had in fact issued the token originally.
  • callback is a part to call after encoding is done, should one wish to handle encoding the token asynchronously.

(You can also read about more details on options and how to use public-key cryptography instead of a shared secret key.)

Athwart 6 JWT Integration

To make Angular half-dozen piece of work with our JWT is quite unproblematic using angular-jwt. Simply add together the post-obit to client/src/app/app.modules.ts:

          import { JwtModule } from '@auth0/angular-jwt'; // ... export function tokenGetter() {   return localStorage.getItem('access_token'); }  @NgModule({ // ...   imports: [     BrowserModule,     AppRoutingModule,     HttpClientModule,     FormsModule,     // Add this import here     JwtModule.forRoot({       config: {         tokenGetter: tokenGetter,         whitelistedDomains: ['localhost:4000'],         blacklistedRoutes: ['localhost:4000/api/auth']       }     })   ], // ... }                  

That is basically all that is required. Of course, we have some more code to add to do the initial authentication, but the angular-jwt library takes care of sending the token forth with every HTTP request.

  • The tokenGetter() function does exactly what it says, but how it is implemented is entirely up to you lot. Nosotros have chosen to return the token that nosotros save in localStorage. Y'all are of class free to provide any other method you want, every bit long as it returns the JSON web token encoded string.
  • The whiteListedDomains option exists so you tin can restrict which domains the JWT gets sent to, so public APIs don't receive your JWT besides.
  • The blackListedRoutes choice allows y'all to specify specific routes that shouldn't receive the JWT even if they are on a whitelisted domain. For instance, the authentication endpoint doesn't need to receive it because there's no point: The token is typically zilch when it'due south chosen anyway.

Making It All Work Together

At this point, we have a manner to generate a JWT for a given user using the /auth endpoint on our API, and we have the plumbing done on Angular to send a JWT with every HTTP request. Not bad, but you might point out that absolutely nothing has changed for the user. And you would be correct. We can still navigate to every page in our app, and we can call whatever API endpoint without even sending a JWT. Not adept!

We need to update our client app to be concerned near who is logged in, and also update our API to crave a JWT. Let's go started.

We'll demand a new Angular component for logging in. For the sake of brevity, I'll continue this every bit unproblematic equally possible. We'll as well need a service that will handle all our Authentication requirements, and an Athwart Baby-sit to protect the routes that shouldn't exist attainable before logging in. Nosotros'll practice the following in the client application context.

          cd customer ng yard component login --spec=false --inline-style ng thousand service auth --flat --spec=faux ng thousand guard auth --flat --spec=faux                  

This should accept generated four new files in the client folder:

          src/app/login/login.component.html src/app/login/login.component.ts src/app/auth.service.ts src/app/auth.guard.ts                  

Next, we need to provide the authentication service and guard for our app. Update client/src/app/app.modules.ts:

          import { AuthService } from './auth.service'; import { AuthGuard } from './auth.guard';  // ...  providers: [   TodoService,   UserService,   AuthService,   AuthGuard ],                  

And then update the routing in client/src/app/app-routing.modules.ts to brand apply of the hallmark guard and supply a route for the login component.

          // ... import { LoginComponent } from './login/login.component'; import { AuthGuard } from './auth.baby-sit';  const routes: Routes = [   { path: 'todos', component: TodoListComponent, canActivate: [AuthGuard] },   { path: 'users', component: UserListComponent, canActivate: [AuthGuard] },   { path: 'login', component: LoginComponent},   // ...                  

Finally, update client/src/app/auth.guard.ts with the post-obit contents:

          import { Injectable } from '@angular/core'; import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';  @Injectable() export course AuthGuard implements CanActivate {   constructor(private router: Router) { }    canActivate(adjacent: ActivatedRouteSnapshot, land: RouterStateSnapshot) {     if (localStorage.getItem('access_token')) {       return true;     }      this.router.navigate(['login']);     return false;   } }                  

For our demo application, we are simply checking for the existence of a JWT in local storage. In real-world applications, you would decode the token and bank check its validity, expiration, etc. For example, you could employ JwtHelperService for this.

At this bespeak, our Angular app will now ever redirect yous to the login page since nosotros have no way to log in. Let's rectify that, starting with the authentication service in client/src/app/auth.service.ts:

          import { Injectable } from '@angular/core'; import { HttpClient } from '@athwart/common/http'; import { Appreciable } from 'rxjs'; import { map } from 'rxjs/operators';  @Injectable() export class AuthService {   constructor(private http: HttpClient) { }    login(username: cord, password: string): Observable<boolean> {     return this.http.post<{token: string}>('/api/auth', {username: username, password: password})       .pipage(         map(consequence => {           localStorage.setItem('access_token', result.token);           return true;         })       );   }    logout() {     localStorage.removeItem('access_token');   }    public get loggedIn(): boolean {     return (localStorage.getItem('access_token') !== null);   } }                  

Our authentication service has only two functions, login and logout:

  • login Posts the provided username and countersign to our back end and sets the access_token in localStorage if it receives one back. For the sake of simplicity, there is no mistake handling here.
  • logout simply clears access_token from localStorage, requiring a new token to be acquired earlier anything farther can be accessed again.
  • loggedIn is a boolean property we can quickly use to determine if the user is logged in or not.

And lastly, the login component. These have no relation to really working with JWT, so experience free to copy and paste into client/src/app/login/login.components.html:

          <h4 *ngIf="error">{{error}}</h4> <class (ngSubmit)="submit()">   <div class="class-grouping col-three">     <label for="username">Username</characterization>     <input type="text" name="username" class="form-control" [(ngModel)]="username" />   </div>   <div form="course-group col-three">     <label for="password">Password</label>     <input blazon="countersign" proper noun="password" class="class-control" [(ngModel)]="countersign" />   </div>   <div class="form-grouping col-3">     <button grade="btn btn-primary" type="submit">Login</button>   </div> </form>                  

And client/src/app/login/login.components.ts will need:

          import { Component, OnInit } from '@athwart/cadre'; import { AuthService } from '../auth.service'; import { Router } from '@angular/router'; import { first } from 'rxjs/operators';  @Component({   selector: 'app-login',   templateUrl: './login.component.html' }) export class LoginComponent {   public username: string;   public countersign: string;   public error: string;    constructor(private auth: AuthService, private router: Router) { }    public submit() {     this.auth.login(this.username, this.countersign)       .pipe(get-go())       .subscribe(         result => this.router.navigate(['todos']),         err => this.error = 'Could not cosign'       );   } }                  

VoilĂ , our Angular 6 login example:

The login screen of our sample Angular Todo List app.

At this phase, we should be able to log in (using jemma,paul, or sebastian with the password todo) and see all the screens over again. But our application shows the same navigation headers and no way to log out regardless of the current state. Allow's ready that earlier nosotros move on to fixing our API.

In client/src/app/app.component.ts, replace the whole file with the following:

          import { Component } from '@angular/cadre'; import { Router } from '@angular/router'; import { AuthService } from './auth.service';  @Component({   selector: 'app-root',   templateUrl: './app.component.html',   styleUrls: ['./app.component.css'] }) export class AppComponent {   constructor(private auth: AuthService, individual router: Router) { }    logout() {     this.auth.logout();     this.router.navigate(['login']);   } }                  

And for client/src/app/app.component.html replace the <nav> section with the following:

                      <nav class="nav nav-pills">     <a class="nav-link" routerLink="todos" routerLinkActive="active" *ngIf="auth.loggedIn">Todo List</a>     <a grade="nav-link" routerLink="users" routerLinkActive="active" *ngIf="auth.loggedIn">Users</a>     <a class="nav-link" routerLink="login" routerLinkActive="active" *ngIf="!auth.loggedIn">Login</a>     <a course="nav-link" (click)="logout()" href="#" *ngIf="auth.loggedIn">Logout</a>   </nav>                  

Nosotros have fabricated our navigation context-aware that it should only brandish certain items depending on whether the user is logged in or not. auth.loggedIn tin can, of course, be used anywhere you can import the authentication service.

Securing the API

You may be thinking, this is great… everything looks to exist working wonderfully. Just attempt logging in with all iii of the different usernames, and yous'll notice something: They all return the same todo listing. If we have a look at our API server, we can meet that each user does, in fact, have their own listing of items, and so what's upwardly?

Well, retrieve when nosotros started out, nosotros coded our /todos API endpoint to always return the todo listing for userID=ane. This was considering we didn't take whatever way of knowing who was the currently logged-in user.

Now we exercise, so permit'due south see how easy it is to secure our endpoints and apply the information encoded in the JWT to provide the required user identity. Initially, add together this one line to your server/app.js file right beneath the last app.utilize() call:

          app.use(expressJwt({hush-hush: 'todo-app-super-shared-secret'}).unless({path: ['/api/auth']}));                  

We employ the express-jwt middleware, tell it what the shared underground is, and specify an array of paths it shouldn't require a JWT for. And that's it. No demand to touch each and every endpoint, create if statements all over, or anything.

Internally, the middleware is making a few assumptions. For case, it assumes the Authorization HTTP header is post-obit the mutual JWT blueprint of Bearer {token}. (The library has lots of options though for customizing how it works if that's not the instance. See express-jwt Usage for more details.)

Our second objective is to use the JWT encoded information to find out who is making the call. Once again limited-jwt comes to the rescue. Every bit part of reading the token and verifying it, information technology sets the encoded payload we sent in the signing procedure to the variable req.user in Express. Nosotros can then use information technology to immediately admission whatever of the variables we stored. In our example, we set userID equal to the authenticated user'due south ID, and every bit such we can use it direct equally req.user.userID.

Update server/app.js again, and change the /todos endpoint to read as follows:

          res.send(getTodos(req.user.userID));                  
Our Angular Todo List app leveraging the JWT to show the todo list of the logged-in user, rather than the one we had hardcoded earlier.

And that's it. Our API is at present secured against unauthorized access, and nosotros can safely make up one's mind who our authenticated user is in whatever endpoint. Our client awarding as well has a simple authentication procedure, and any HTTP services we write that call our API endpoint will automatically have an hallmark token attached.

If you cloned the Github repository, and simply want to run across the cease issue in action, you lot can bank check out the code in its final form using:

          git checkout with-jwt                  

I hope you've found this walkthrough valuable for adding JWT authentication to your own Angular apps. Thanks for reading!

Auth0 Error Cannot Read Property Crypto of Undefined

Source: https://www.toptal.com/angular/angular-6-jwt-authentication

1 Response to "Auth0 Error Cannot Read Property Crypto of Undefined"

  1. Over the past few months, mymonero app investors have seen tremendous volatility, the kind that has rearely been experienced with any other type of investment. It was just a few months ago that industry leader Bitcoin saw its price rise from $2,000 per coin to almost $20,000 per coin. Of course, the gains were short lived as the price has recently fallen back as low as $6,000.

    ReplyDelete

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel