Common Angular services for communicating with backend, authentication and user managing.
In progress...
Ronas IT Angular Common working with cookies. One of the main advantages of this approach is that cookies can be HTTP-only. It makes them read-protected on the client side, that improves safety against any Cross-site scripting (XSS) attacks. Cookie-based authentication allows using this services in Server-Side Rendering (SSR) applications.
Install Ronas IT Angular Common:
npm i @ronas-it/angular-common --save
Add ApiModule
to AppModule
imports:
import { ApiModule } from '@ronas-it/angular-common' ;
import { configuration } from '@configuration' ;
@NgModule ( {
imports : [
ApiModule . forRoot ( {
apiUrl : configuration . api . url
} ) ,
...
] ,
...
} )
export class AppModule { }
Inject ApiService
and use it:
import { ApiService } from '@ronas-it/angular-common' ;
import { Injectable } from '@angular/core' ;
@Injectable ( )
export class ProductService {
constructor (
private apiService : ApiService
) { }
public delete ( id : number ) : Observable < void > {
return this . apiService . delete ( `/products/${ id } ` ) ;
}
...
}
Add CookieModule
to AppModule
imports:
import { CookieModule } from '@ronas-it/angular-common' ;
@NgModule ( {
imports : [
CookieModule . forRoot ( {
defaultOptions : { path : '/' , /* other cookie options ... */ }
} ) ,
...
] ,
...
} )
export class AppModule { }
Inject CookieService
and use it:
import { BehaviorSubject , Subject } from 'rxjs' ;
import { CookieService } from '@ronas-it/angular-common' ;
import { Injectable } from '@angular/core' ;
@Injectable ( )
export class CookiePopupFacade {
private isCookiesAccepted$ : Subject < boolean > ;
constructor (
private cookieService : CookieService
) {
this . isCookiesAccepted$ = new BehaviorSubject ( this . cookieService . get ( 'isCookiesAccepted' ) === 'true' ) ;
}
public acceptCookies ( ) : void {
this . isCookiesAccepted$ . next ( true ) ;
this . cookieService . put ( 'isCookiesAccepted' , 'true' , { maxAge : 4e10 } ) ;
}
...
}
(SSR Only) Add providers for REQUEST
and RESPONSE
injection tokens from
@nguniversal/express-engine/tokens
in server.ts
:
server . get ( '*' , ( req , res ) => {
res . render ( indexHtml , {
req,
res,
providers : [
{
provide : APP_BASE_HREF ,
useValue : req . baseUrl
} ,
{
provide : REQUEST ,
useValue : req
} ,
{
provide : RESPONSE ,
useValue : res
}
]
} ) ;
} ) ;
(SSR Only) Set requestToken
and responseToken
parameters in the CookieModule
config:
import { CookieModule } from '@ronas-it/angular-common' ;
import { REQUEST , RESPONSE } from '@nguniversal/express-engine/tokens' ;
@NgModule ( {
imports : [
CookieModule . forRoot ( {
defaultOptions : { /* ... */ } ,
requestToken : REQUEST ,
responseToken : RESPONSE
} ) ,
...
] ,
...
} )
export class AppModule { }
Note: This module depends on ApiModule
and AuthModule
. Please make sure to
install them prior to installing this module.
Create a User
model and extend it from AbstractUser
:
import { AbstractUser } from '@ronas-it/angular-common' ;
import { Expose } from 'class-transformer' ;
export class User extends AbstractUser {
@Expose ( { groups : [ 'main' ] } )
public id : number ;
@Expose ( { groups : [ 'main' ] } )
public name : string ;
@Expose ( { groups : [ 'main' ] } )
public email : string ;
}
Create a UserService
and extend it from CommonUserService
:
import { UserService as CommonUserService } from '@ronas-it/angular-common' ;
@Injectable ( )
export class UserService extends CommonUserService < User > {
/* Define custom methods or override existing methods here. */
}
Create a UserModule
and add CommonUserModule
to imports:
import { NgModule } from '@angular/core' ;
import { User } from './models' ;
import { UserModule as CommonUserModule } from '@ronas-it/angular-common' ;
import { UserService } from './user.service' ;
@NgModule ( {
imports : [
CommonUserModule . forRoot ( {
userModel : User ,
userService : UserService
} ) ,
...
] ,
...
} )
export class UserModule { }
Inject UserService
and use it:
import { Action } from '@ngrx/store' ;
import { Actions , createEffect , ofType } from '@ngrx/effects' ;
import { AuthService } from '@shared/auth' ;
import { catchError , filter , mergeMap , switchMap , withLatestFrom } from 'rxjs/operators' ;
import { HttpErrorResponse } from '@angular/common/http' ;
import { Injectable } from '@angular/core' ;
import { Observable , of } from 'rxjs' ;
import { User } from '../models' ;
import { userActions } from './actions' ;
import { UserService } from '../user.service' ;
@Injectable ( )
export class UserEffects {
public refreshProfile$ : Observable < Action > = createEffect (
( ) => this . actions$ . pipe (
ofType ( userActions . refreshProfile ) ,
withLatestFrom ( this . authService . isAuthenticated$ ) ,
filter ( ( [ _ , isAuthenticated ] ) => isAuthenticated ) ,
switchMap ( ( ) => {
return this . userService . refreshProfile ( )
. pipe (
mergeMap ( ( user : User ) => [
userActions . updateProfile ( { profile : user } ) ,
userActions . refreshProfileSuccess ( )
] ) ,
catchError ( ( response : HttpErrorResponse ) => of ( userActions . refreshProfileFailure ( { response } ) ) )
) ;
} )
)
) ;
constructor (
private actions$ : Actions ,
private authService : AuthService ,
private userService : UserService
) { }
}
Note: This module depends on ApiModule
, CookieModule
and UserModule
. Please make sure to
install them prior to installing this module.
Create an AuthService
and extend it from CommonAuthService
:
import { AuthService as CommonAuthService } from '@ronas-it/angular-common' ;
import { Injectable } from '@angular/core' ;
import { User } from '@shared/user' ;
@Injectable ( )
export class AuthService extends CommonAuthService < User > {
/* Define custom methods or override existing methods here. */
}
Create an AuthModule
and add CommonAuthModule
to imports:
import { AuthModule as CommonAuthModule } from '@ronas-it/angular-common' ;
import { AuthService } from './auth.service' ;
import { configuration } from '@configuration' ;
import { NgModule } from '@angular/core' ;
@NgModule ( {
imports : [
CommonAuthModule . forRoot ( {
unauthorizedRoutes : configuration . api . unauthorized_routes ,
authService : AuthService ,
// Optionally, you can pass `unauthenticatedRoute` parameter that
// specifies the route to redirect to after logout or when a user is
// not authenticated to view some page. By default it is set to `/login`.
unauthenticatedRoute : '/'
} ) ,
...
] ,
...
} )
export class AuthModule { }
Inject AuthService
and use it:
import { Action } from '@ngrx/store' ;
import { Actions , createEffect , ofType } from '@ngrx/effects' ;
import { authActions } from './actions' ;
import { AuthService } from '../auth.service' ;
import { catchError , exhaustMap , map } from 'rxjs/operators' ;
import { Injectable } from '@angular/core' ;
import { Observable , of } from 'rxjs' ;
@Injectable ( )
export class AuthEffects {
public authorize$ : Observable < Action > = createEffect (
( ) => this . actions$ . pipe (
ofType ( authActions . authorize ) ,
exhaustMap ( ( action ) => {
return this . authService
. authorize ( action . credentials )
. pipe (
map ( ( response ) => authActions . authSuccess ( { response } ) ) ,
catchError ( ( response ) => of ( authActions . authFailure ( { response } ) ) )
) ;
} )
)
) ;
constructor (
private actions$ : Actions ,
private authService : AuthService
) { }
}
ApiModule . forRoot ( config : ApiConfig )
Name
Type
Required
Description
apiUrl
string
Yes
Endpoint that allows you to access an API
trailingSlash
boolean
No
The need for trailing slash (https://api.your-service.com/login/
for example)
enableArrayKeys
boolean
No
Enabling array keys for http params
fileKeys
Array<string>
No
List of the file keys for http params
Field
Type
apiUrl
string
trailingSlash
string
fileKeys
Array<string>
Method
Arguments
Return type
get<T>
endpoint: string, params: any, options: object
Observable<T>
post<T>
endpoint: string, data: any, options: object
Observable<T>
put<T>
endpoint: string, data: any, options: object
Observable<T>
delete<T>
endpoint: string, params: any, options: object
Observable<T>
CookieModule . forRoot ( config : CookieConfig )
Name
Type
Required
Description
defaultOptions
CookieOptions
No
Cookie options that will be used if not specified in the put
method
requestToken
InjectionToken<Request>
No
Request
injection token from @nguniversal/express-engine/tokens
for cookies support in SSR
responseToken
InjectionToken<Response>
No
Response
injection token from @nguniversal/express-engine/tokens
for cookies support in SSR
Name
Type
maxAge
number
expires
Date
path
string
domain
string
secure
boolean
sameSite
boolean | 'lax' | 'strict' | 'none'
CookieService<TKey extends string = string>
Field
Type
cookieString
string
Method
Arguments
Return type
get
key: TKey
string | null
getObject
key: TKey
object | null
getAll
Record<string, string>
hasKey
key: TKey
boolean
put
key: TKey, value: string, options?: CookieOptions
void
putObject
key: TKey, value: object, options?: CookieOptions
void
remove
key: TKey, options?: CookieOptions
void
removeAll
options?: CookieOptions
void
CommonAuthModule . forRoot ( config : AuthConfig )
Name
Type
Required
Description
unauthorizedRoutes
Array<string>
Yes
Routes that don't need authorization (public routes, e.g. login, registration and forgot password pages)
authService
new (...args: Array<any>) => any
Yes
Service that will be used in your app
unauthenticatedRoute
string
No
Route to redirect to after logout or when a user is not authenticated to view some page. By default it is set to /login
disableRedirectAfterUnauthorize
boolean
No
Whether to redirect to unauthenticatedRoute
after logout or when a user is not authenticated to view some page. By default it is set to false
authenticatedRoute
string
No
Route to redirect after successful login
loginEndpoint
string
No
Endpoint for login, e.g. /api/token
refreshTokenEndpoint
string
No
Endpoint for refreshing token, e.g. /api/token/refresh
refreshTokenEndpointMethod
'get' | 'post'
No
HTTP Method that will be used for calling endpoint to refresh token
isAuthenticatedField
string
No
Field for cookie
rememberField
string
No
Field for cookie
cookiesExpirationDays
number
No
Expiration for authentication cookies when call authorize with remember flag set to true. By default it is set to 365
AuthService<User extends AbstractUser>
Static constant
Type
DEFAULT_LOGIN_ENDPOINT
string
DEFAULT_UNAUTHENTICATED_ROUTE
string
DEFAULT_IS_AUTHENTICATED_FIELD
string
DEFAULT_REFRESH_TOKEN_ENDPOINT
string
DEFAULT_REMEMBER_FIELD
string
DEFAULT_COOKIES_EXPIRATION_DAYS
number
Field
Type
isTokenRefreshing$
Observable<boolean>
isAuthenticated$
Observable<boolean>
cookiesExpiresDate
Date
Method
Arguments
Return type
authorize<T>
credentials: AuthCredentials & T, remember: boolean
Observable<AuthResponse<User>>
manuallyAuthorize
authResponse: object, remember: boolean = true
Observable<AuthResponse<User>>
unauthorize
void
refreshToken
Observable<HttpResponse<void>>
setIsAuthenticated
remember: boolean
void
resetIsAuthenticated
void
resetRemember
void
Field
Type
Required
email
string
No
password
string
Yes
AuthResponse<User extends AbstractUser>
Field
Type
Required
user
User
No
UserModule . forRoot ( config : UserConfig )
Name
Type
Required
Description
userModel
new (user: any) => any
Yes
Model (class) for user
userService
new (...args: Array<any>) => any
Yes
Your UserService implementation
profileRelations
Array<string>
No
Relations for getting profile request. For example: /profile?with[]=company&with[]=clients
profileRelationsKey
string
No
with
by default
UserService<User extends AbstractUser>
Field
Type
profile$
Observable<User>
Method
Arguments
Return type
refreshProfile
Observable<User>
loadProfile
Observable<User>
updateProfile
user: User
Observable<void>
updatePassword
userPasswords: UserPasswords
Observable<void>
setProfile
user: User
void
patchProfile
user: Partial<User>
void
resetRemember
void
resetProfile
void
userToPlain
user: User, options?: ClassTransformOptions
Object
plainToUser
plain: object, options?: ClassTransformOptions
User
Field
Type
Required
id
number | string
Yes
Contributions to Ronas IT Angular Common are welcome. The contribution guide can be found in the Contributing guide .
Ronas IT Angular Common is open-sourced software licensed under the MIT license .