Angular — Tip how to avoid annoying authorization on local development

Constantly entering your login and password can be annoying. In most cases to connect secured backend you only need a valid token. Following this trail, master of backend can produce to us non-expired token. Even of that you can have dockerize your front-end project so clone it and inject valid token and forget about any credentials! Easily start to new programmers in your project.

In my daily cases, our backend developer created us multiple non-expired token to different user to check if our page is properly behave. So I’ve created simply node script to put token from terminal to code. I will describe it below. But the most important case is to use different class for authorization handler. One for local development which avoid whole login logic and second with proper logic to production env.

Thanks to kindness of angular CLI, we have the special file angular.json where we can create special configuration build. Also it can be applied to ng serve.

So as I mention about angular.json, start with it. For now, the most important functionality is ability to replace file. In configuration section, create new set of properties for local-dev and of course: fileReplacements. What’s more in serve, point specific config. On below, every place where I used token-development, must be change for your project name.

{
...
"projects": {
"token-development": {
...
"architect": {
"build": {
...
"configurations": {
"local-dev": {
"optimization": false,
"sourceMap": true,
"namedChunks": true,
"aot": false,
"vendorChunk": true,
"buildOptimizer": false,
"serviceWorker": true,
"fileReplacements": [
{
"replace": "src/app/shared/services/authorization.service.ts",
"with": "src/local-development/authorization-local.service.ts"
}
]
},
...
}
},

"serve": {
...
"configurations": {
"production": {
"browserTarget": "token-development:build:production"
},
"local-dev": {
"browserTarget": "token-development:build:local-dev"
}

}
}
}
}
},
"defaultProject": "token-development"
}

In package.json, slightly change start command in scripts to point that ng serve should use specific configuration.

"start": "ng serve -c local-dev",

Next step is basic node script which create file or modify exist to put the latest token. I’m using fs-extra module to manipulate the file. So before run that script, we must got fs-extra via npm:

npm install fs-extra --save-dev

and save the script in your_project_path/script/update-token.js

const {resolve, relative} = require('path');
const {writeFileSync} = require('fs-extra');

const args = process.argv.slice(2);

if (args.length !== 1) {
console.error('Wrong number of parameters! Check if you put token properly!');
}

const token = args[0];

const file = resolve(__dirname, '..', 'src', 'local-development', 'local-token.ts');
writeFileSync(
file,
`
/**
* IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT!
* THIS FILE HAS TOKEN FOR LOCAL DEVELOPMENT PURPOSE.
*/
/* tslint:disable */
export const localToken = '${token}';
/* tslint:enable */
`,
{encoding: 'utf-8'}
);

console.log(`You create file with your token ${relative(resolve(__dirname, '..'), file)}`);

To easily run that script put it to package.json in scripts:

"update-token": "node scripts/update-token.js"

Next step is to add start up service to angular project and implement fake-auth class. So let’s go… First create start up service. Before show any components of our awesome page, Angular check if all resources are available to show everything proper. Eg. if our user has already token, if not show page with login, login with OpenID Connect, facebook, google or other provider.

@Injectable()
export class StartUpService {

constructor(private auth: AuthorizationService) {
}

load() {
return new Promise((resolve, reject) => {
this.auth.getUserInfo().subscribe(() => {
resolve(true);
},
() => {
window.location.href = this.getUnauthPage();
reject(false);
}
);
});
}

/**
* show page with some information that user has not access to service.
*/
private getUnauthPage(): string {
return `${environment.domain}/assets/unauthorized/unauthorized.html`;
}
}

Also to work with that class, point it in app.module.ts in providers:

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,

],
providers: [
StartUpService,
AuthorizationService,
{
provide: APP_INITIALIZER,
useFactory: startUpFactory,
deps: [StartUpService, HttpClientModule],
multi: true
}
],
bootstrap: [AppComponent]
})

export class AppModule {
}

export function startUpFactory(startupService: StartUpService): Function {
return () => startupService.load();
}

Now we will create very simple Authorization class. In my cases, I show user the login page provided by OIDC and getting token from that external provider. For our purpose it can be some nodejs server which getting username and password and response with token.

@Injectable()
export class AuthorizationService {

public get authorizationData(): AuthResponse {
return this._authorizationData;
}

private _authorizationData: AuthResponse;

constructor(private httpClient: HttpClient) {
}

getUserInfo(): Observable<AuthResponse> {
// here you can send user and password in real server...
const body = {username: 'username', password: 'password'};
return this.httpClient.post('localhost:3000/token', body)
.pipe(
tap((res: AuthResponse) => {
this._authorizationData = res;
})
);
}
}

As far is good. But for now nothing new from my side. In title I said that we will change the file with “normal” logic of login to fake one where store the token. In our project workspace create a new folder: src/local-development/authorization-local.service.ts. That path must be the same as you put in your angular.json file. Also here I put the imports, because it’s tricky part with getting localToken. As Angular builder move that file and replace with exist one in our service catalog, the path to file where we store token must be absolute.

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { AuthResponse } from 'src/app/shared/interfaces/AuthResponse';
import { localToken } from 'src/local-development/local-token';


@Injectable()
export class AuthorizationService {
public get authorizationData(): AuthResponse {
return this._authorizationData;
}

private _authorizationData: AuthResponse = {
token: localToken,
expires_in: null,
userName: 'devUser',
email: 'local-user@gmail.com'
};

constructor() {
}

getUserInfo(): Observable<AuthResponse> {
console.warn('WARNING: LOCAL FILE, THAT INFORMATION SHOULD NOT SEE ON PRODUCTION', this.authorizationData);
return of(this._authorizationData);
}
}

Now run our hiper, super node script to generate the very first token. My advice is to run the command in that way:

npm run update-token none

Unfortunately, if we would like to run ng build without the local-token.ts, got an error that authorization class is importing file but it’s not exist. So I decided to push the empty token to github and use extra command for github to non tracking that file any more. I think, from security reasons, we wouldn’t like to keep any token in github repository, especially that one which never expired!

git update-index --skip-worktree src/local-development/local-token.ts

From my perspective it’s very useful approach to working on local machine and if you need change the token you put the command in terminal and going on. I hope you also like it.

The boilerplate of fully working project is on my github repo: https://github.com/konri/angular_token_dev

PS: It’s my first article in my life so if you have any thoughts, I will strongly appreciate that!