Skip to content

Authentication package for angular 5 based on HttpClientModule

License

Notifications You must be signed in to change notification settings

juztcode/angular-auth

Repository files navigation

Build Status npm Version npm License

@juztcode/angular-auth is a library that can be used to implement authentication workflow of your angular 5+ application. This library uses HttpClient to send http requests.

Click here to find a working demo.

Contents

Key features

  • Login, logout, check status
  • Authenticate requests
  • Decode token
  • Mange access using user permissions
  • Refresh expired tokens using refresh tokens

Installation

npm install @juztcode/angular-auth --save

Note: This will install package and save the dependency in your package.json file.

Configure project

To use the library in your application you need to import the AuthModule in your AppModule and provide configuration.

import {AuthModule} from '@juztcode/angular-auth';

const authConfig: AuthConfig = {
  persistTokensEnabled: true,
  refreshTokenEnabled: false, //optional feature see: Use refresh tokens
  userPermissionsEnabled: false, //optional feature see: Use user permissions
  loginUrl: LOGIN_URL,
  accessTokenExpiredResponseStatus: 403,
  accessTokenExpiredErrorCode: 4001,
};

@NgModule({
  imports: [
    AuthModule.forRoot(authConfig)
  ]
})
export class AppModule {}

To view response type click here

Use authentication

Use AuthProvider

To use auth features you need to import AuthProvider.

import {AuthProvider} from '@juztcode/angular-auth';

@Injectable()
export class LoginComponent {
  constructor(private authProvider: AuthProvider){}
}

login(requestBody: any): Promise

This will send the login and save the auth inside the application and persist it. If the permissions are enabled this will also set the permission using the userRoleId.

@parameter {any} requestBody - set as the request body of the login request

let requestBody = {username: 'test', password: '1234'}

@return {Promise} - If the login is success send true else false.

setAuth(auth; Auth): Promise

This function can be used to save Auth retrieved by any other request. (eg: user role change request)

@parameter {Auth} auth - auth retrieved

let auth = {accessToken: 'access-token', refreshToken: 'refresh-token'}

@return {Promise} - If the auth save is success return true else false.

logOut(): Promise

Can use to logout from the application. This will remove the auth from memory and also from storage.

isLoggedIn(): Promise

Can use this function to check if the user is logged in. If the token is present in the storage this will save the token inside the provider.

getAccessToken(): string

Return the accessToken (instance variable in the AuthProvider)

Note: This will only accessible after a successful 'login' or after a successful 'isLoggedIn;

getRefreshToken: string

Return the refreshToken (instance variable in the AuthProvider)

Note: This will only accessible after a successful 'login' or after a successful 'isLoggedIn'

getValueInToken(key: string): T

@parameter {string} key - key of the attribute that need to retrieve (eg: 'userId') @parameter T - type the return need to be casted to

Send authenticated requests

If you send http requests using HttpClient then the authentication header is appended. Tokens are added under authorization tag. To view configuration and headers click here

import {HttpClient} from '@angular/common/http';

export class Service {
  constructor (private http: HttpClient){}
  
  getDetails(){
    this.http.get('https://test-angular-packages.herokuapp.com/secured');
  }
}

Use permissions

To view configurations and how to set permission data set click here.

Use PermissionsProvider

To use permissions you need to import PermissionProvider.

import {PermissionProvider} from '@juztcode/angular-auth';

@Injectable()
export class HomeComponent {
  constructor(private permissionProvider: PermissionProvider){}
}

setPermissionByUserRoleId(userRoleId: number): void

This will update the permission inside the PermissionProvider and affect all the places permissions are in use. After the login the permissions are set. You can use this to save permissions at any other place (eg: convert user role)

@parameter {number} userRoleId - id of the user role

Note: This user role id should be a one in the permission data set

getPermissions(): any

This will return the permissions object inside PermissionsProvider and you can use it to define the user access.

Use refresh tokens

To view configuration and how to enable refresh tokens click here

Note: When a request is made and the access token expired response received then the request will retry after getting new token pair.

Add additional global headers

You can add additional global headers in every request. See how to configure click here

Configuration options

When importing the AuthModule you can provide two configurations. AuthConfig and AuthOptionalConfig. AuthConfig is a mandatory while AuthOptionalConfig is a optional and has a default value.

Main options

export interface AuthConfig {
  persistTokensEnabled: boolean;
  loginUrl: string;
  userPermissionsEnabled: boolean;
  getPermissionUrl?: string;
  permissionDataSet?: UserPermissions[];
  refreshTokenEnabled: boolean;
  refreshTokenUrl?: string;
  accessTokenExpiredResponseStatus: number;
  accessTokenExpiredErrorCode: number;
  refreshTokenExpiredResponseStatus?: number;
  refreshTokenExpiredErrorCode?: number;
}

Login options

  • persistTokensEnabled - If true the tokens are saved in the storage and tokenGetter, tokenSetter and tokenRemover functions also need to provide. If those any of those functions are not provided the persistTokensEnabled will be false and hence the tokens are not saved in the storage.

  • loginUrl - Url used in login and the request must receive a Auth response and then the accessToken and refreshToken are saved.

export interface Auth {
  accessToken: string;
  refreshToken: string;
}

User permission options

  • userPermissionEnabled - Enable userRole based user permissions. To use this you need to have the userRoleId inside the token and you can configure the attribute name (see: AuthOptionalConfig). The user permissions are stored in the PermissionProvider and can be mapped inside any component. When the login is success the new permissions are set usign the userRoleId inside the token.

There are two ways to set permissions.

  • getPermissionUrl (optional) - This use a url to get permissions. (see: UserPermission type)
  • permissionDataSet (optional) - Use predefined permissions. (see: UserPermission type)
export interface UserPermissions {
  userRoleId: number;
  permissions: any;
}

Refresh token options

  • refreshTokenEnabled - Enable refresh token workflow. If the access token is expired then it try to get new token pair using refresh token and then retry the request again. You need to provide token expired error codes.

  • refreshTokenUrl (optional) - Use to get new token pair using refresh token. Auth response should be send with new token pair.

  • accessTokenExpiredResponseStatus - Response status when access token expired. (used to retry the response)

  • accessTokenExpiredErrorCode - You need to send error code inside the access token expired response. (used to retry the response)

Note: Token interceptor will check error response for above conditions and retry the response. If refresh token not enabled user will logout

  • refreshTokenExpiredResponseStatus(optional/depend on refreshTokenEnabled) - Response status when refresh token expired
  • refreshTokenExpiredErrorCode(optional/depend on refreshTokenEnabled) - You need to send error code inside the refresh token expired response.

Note: If a refresh token expired response received the user will logout.

Additional options

Those are the additional configurations default values are used and you can override them.

export const DEFAULT_ADDITIONAL_AUTH_CONFIG: AuthConfigAdditional = {
  accessTokenHeaderName: 'Authorization',
  accessTokenPrefix: 'Bearer',
  refreshTokenHeaderName: 'Authorization',
  refreshTokenPrefix: 'Basic',
  tokenInterceptorExcludedUrls: [],
  accessTokenStorageKey: 'access-token',
  refreshTokenStorageKey: 'refresh-token',
  userRoleIdKey: 'userRoleId',
  globalHttpHeaders: [],
  tokenGetter: async (tokenName: string) => localStorage.getItem(tokenName),
  tokenSetter: async (tokenName: string, token: any) => localStorage.setItem(tokenName, token),
  tokenRemover: async (tokenName: string) => localStorage.removeItem(tokenName),
  convertToAuthType: (auth: Auth) => auth,
  convertToUserPermissionType: (userPermissions: UserPermissions) => userPermissions,
  convertToApiErrorType: (apiError: ApiError) => apiError
};

Basic options

  • accessTokenHeaderName - Header name use when appending access token in each request.

  • accessTokenPrefix - Prefix used when appending token (eg: Bearer {access-token})

  • refreshTokenHeaderName - Header name use when appending refresh token in refresh request (see: refreshTokenUrl in AuthConfig).

  • refreshTokenPrefix - Prefix used when appending token (eg: Basic {refresh-token})

  • tokenInterceptorExcludedUrls - These urls are excluded when appending the access token. (eg: Login url)

  • globalHttpHeaders - You can provide a list of additional http headers that need to be appended. Also you can provide excluded method list.

const data = [
  {key: 'Content-Type', value: 'application/json', excludedMethods: ['GET']}
]

Note: excludedMethods array is optional.

Note: Login url is automatically added to the excluded list

  • accessTokenStorageKey = key used when storing the access-token in key value pair.
  • refreshTokenStorageKey = key used when storing the refresh-token in key value pair.
  • userRoleIdKey - attribute name related to the userRoleId inside the access-token payload.

Note: To use permissions feature you need to have userRoleId inside the token. The user role id must be a one present in the permission data set.

sample permission data set

[
  {
    "userRoleId": 1,
    "permissions": {"canAccess": true, "homePage": "Dashboard"}
  },
  {
    "userRoleId": 2,
    "permissions": {"canAccess": false, "homePage": "Profile"}
  }
]

sample token payload

{
  "userId": 100,
  "userRoleId": 1
}
  • tokenGetter, tokenSetter, tokenRemover - If you plan to use different storage methods you can override default functions eg: If you have a storage provider and need to use that
const storage = new StorageProvider(); //you need to create a instance of the provider manually to use it inside AppModule.

async (tokenName: string) => {
  return storage.read(tokenName); //your method call
}

Advanced options

You can configure the auth response type, permission response type and api error response type. This library uses functions to extract relevant type from response.

  • convertToAuthType - This function used to convert the response to the Auth type

Note: If not supplied the default function is used

(data: Auth) => {
  return data;
}

If the auth type sent from the server is different you can define a function to extract the Auth type If the auth type from server

{
  "bearer": "access-token", 
  "basic": "refresh-token"
}

Then the function

(data: any) => {
  const auth: Auth = {accessToken: authFromServer.bearer, refreshToken: authFromServer.basic};
  return auth;
}
  • convertToUserPermissionType - This function used to convert the response to the UserPermission type

Note: If not supplied the default function is used

(data: UserPermissions) => {
  return data;
}

If the user permission type sent from the server is different you can pass a function to extract the UserPermissions type. The library will iterate through the data set sent from the server and apply the function to get the UserPermissions type. If the permission type from server

[
  {
    "role": 1,
    "permission": [true, true, false]
  },
  {
    "role": 2,
    "permissions": [true, false, false]
  }
]

Then the function

(data: any) => {
  const userPermissions: UserPermissions = {
    userRoleId: data.role,
    permissions: {
      canViewHome: data.permissions[0],
      canViewSettings: data.permissions[1],
      canViewInfo: data.permissions[2]
    }
  }
}
  • convertToApiErrorType - This function used to convert the response to the UserPermission type

Note: If not supplied the default function is used

(apiError: ApiError) => {
  return apiError
}

If the error response sent from the server is different you can pass a function to extract the ApiError type. If the error response type from server

{
  "errorResponse": {"errorCode": 4001, "errorMessage": "access token expired."}
}

Then the generator function

(error: any) => {
  const apiError = {
    errorCode: error.errorResponse.errorCode,
    errorMessage: error.errorResponse.errorMessage
  }
}

Help

Repository: https://github.com/juztcode/angular-auth
Issues: https://github.com/juztcode/angular-auth/issues
Demo: https://embed.plnkr.co/qstWVYDhzfY4L4YF5Pxp?p=preview

About

Authentication package for angular 5 based on HttpClientModule

Resources

License

Stars

Watchers

Forks

Packages

No packages published