0% found this document useful (0 votes)
168 views65 pages

Loopback4-Authentication - NPM

This document discusses the loopback4-authentication extension which adds authentication capabilities to LoopBack 4 applications. It supports 7 Passport authentication strategies including OAuth 2.0, HTTP Bearer tokens, local authentication, and social logins with Google, Keycloak, Instagram, Apple, and Facebook. The extension handles authentication strategy implementation while allowing custom verification functions. Usage involves adding the component, binding verification providers, and invoking authentication in request sequences.

Uploaded by

UTIL Desarrollos
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
168 views65 pages

Loopback4-Authentication - NPM

This document discusses the loopback4-authentication extension which adds authentication capabilities to LoopBack 4 applications. It supports 7 Passport authentication strategies including OAuth 2.0, HTTP Bearer tokens, local authentication, and social logins with Google, Keycloak, Instagram, Apple, and Facebook. The extension handles authentication strategy implementation while allowing custom verification functions. Usage involves adding the component, binding verification providers, and invoking authentication in request sequences.

Uploaded by

UTIL Desarrollos
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

loopback4-authentication

node >=8.9
  Dependencies up to date
  @loopback/core ^2.16.0
  @loopback/build ^6.4.0

npm vulnerabilities 0

npm v4.6.0
  License MIT
  Downloads 1.8k/week
  Total Downloads 56k

This is a loopback-next extension for adding authentication layer to a REST


application in loopback 4. This extension is based on the implementation guidelines
provided on official @loopback/authentication page. It provides support for seven
passport based strategies.

1. passport-oauth2-client-password - OAuth 2.0 client password authentication


strategy for Passport. This module lets you authenticate requests containing client
credentials in the request body, as defined by the OAuth 2.0 specification.
2. passport-http-bearer - HTTP Bearer authentication strategy for Passport. This
module lets you authenticate HTTP requests using bearer tokens, as specified
by RFC 6750, in your [Link] applications.
3. passport-local - Passport strategy for authenticating with a username and
password. This module lets you authenticate using a username and password in
your [Link] applications.
4. passport-oauth2-resource-owner-password - OAuth 2.0 resource owner
password authentication strategy for Passport. This module lets you authenticate
requests containing resource owner credentials in the request body, as defined by
the OAuth 2.0 specification.
5. passport-google-oauth2 - Passport strategy for authenticating with Google using
the Google OAuth 2.0 API. This module lets you authenticate using Google in your
[Link] applications.
6. keycloak-passport - Passport strategy for authenticating with Keycloak. This
library offers a production-ready and maintained Keycloak Passport connector.
7. passport-instagram - Passport strategy for authenticating with Instagram using
the Instagram OAuth 2.0 API. This module lets you authenticate using Instagram in
your [Link] applications.
8. passport-apple - Passport strategy for authenticating with Apple using the Apple
OAuth 2.0 API. This module lets you authenticate using Apple in your [Link]
applications.
9. passport-facebook - Passport strategy for authenticating with Facebook using the
Facebook OAuth 2.0 API. This module lets you authenticate using Facebook in
your [Link] applications.

You can use one or more strategies of the above in your application. For each of the
strategy (only which you use), you just need to provide your own verifier function,
making it easily configurable. Rest of the strategy implementation intricacies is
handled by extension.

Install

npm install loopback4-authentication

Quick Starter
For a quick starter guide, you can refer to our loopback 4 starter application which
utilizes all of the above auth strategies from the extension in a simple multi-tenant
application. Refer to the auth module there for specific details on authentication.

Detailed Usage
The first and common step for all of the startegies is to add the component to the
application. See below

// [Link]

export class ToDoApplication extends BootMixin(

ServiceMixin(RepositoryMixin(RestApplication)),

) {

constructor(options: ApplicationConfig = {}) {

super(options);

// Set up the custom sequence

[Link](MySequence);

// Set up default home page

[Link]('/', [Link](__dirname, '../public'));

// Add authentication component

[Link](AuthenticationComponent);

// .... Rest of the code below

Once this is done, you are ready to configure any of the available strategy in the
application.

Oauth2-client-password
First, create an AuthClient model implementing the IAuthClient interface. The purpose
of this model is to store oauth registered clients for the app in the DB. See sample
below.

@model({

name: 'auth_clients',

})

export class AuthClient extends Entity implements IAuthClient {


@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'client_id',

})

clientId: string;

@property({

type: 'string',

required: true,

name: 'client_secret',

})

clientSecret: string;

@property({

type: 'array',

itemType: 'number',

name: 'user_ids',

})

userIds: number[];

constructor(data?: Partial<AuthClient>) {

super(data);

Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same
strategy. You can add your application specific business logic for client auth here.
Here is simple example.

import {Provider} from '@loopback/context';

import {repository} from '@loopback/repository';

import {VerifyFunction} from 'loopback4-authentication';

import {AuthClientRepository} from '../../../repositories';

export class ClientPasswordVerifyProvider

implements Provider<[Link]> {

constructor(

@repository(AuthClientRepository)

public authClientRepository: AuthClientRepository,

) {}

value(): [Link] {

return async (clientId, clientSecret, req) => {

return [Link]({

where: {

clientId,

clientSecret,

},

});

};
}

Please note the Verify function type [Link].

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].OAUTH2_CLIENT_PASSWORD_VERIFIER).
ClientPasswordVerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].

export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

// Inject CLIENT_AUTH_ACTION sequence action provider

@inject(AuthenticationBindings.CLIENT_AUTH_ACTION)

protected authenticateRequestClient: AuthenticateFn<AuthCli


) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

// Perform client authentication here

await [Link](request);

const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticateClient(STRATEGY.CLIENT_PASSWORD, {

passReqToCallback: true

})

@post('/auth/login', {

responses: {

[STATUS_CODE.OK]: {

description: 'Auth Code',

content: {

[CONTENT_TYPE.JSON]: Object,

},

},
},

})

async login(
@requestBody()

req: LoginRequest,

): Promise<{
code: string;

}> {

....
}

For accessing the authenticated AuthClient model reference, you can inject the
CURRENT_CLIENT provider, provided by the extension, which is populated by the
auth action sequence above.

@[Link](AuthenticationBindings.CURRENT_CLIENT)

private readonly getCurrentClient: Getter<AuthClient>,

Http-bearer
First, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is
simple example for JWT tokens.

import {Provider} from '@loopback/context';

import {repository} from '@loopback/repository';

import {verify} from 'jsonwebtoken';

import {VerifyFunction} from 'loopback4-authentication';

import {User} from '../models/[Link]';

export class BearerTokenVerifyProvider

implements Provider<[Link]> {

constructor(

@repository(RevokedTokenRepository)

public revokedTokenRepository: RevokedTokenRepository,

) {}

value(): [Link] {

return async (token) => {

if (token && (await [Link](token


throw new [Link]('Token Revoked');

const user = verify(token, [Link].JWT_SECRET as stri


issuer: [Link].JWT_ISSUER,

}) as User;

return user;

};
}

The above example has an import and injection of a RevokedTokenRepository, which


could be used to keep track of revoked tokens in a datasource like Redis. You can
find an implementation of this repository here and the Redis datasource here.

Please note the Verify function type [Link]

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].BEARER_TOKEN_VERIFIER).toProvider
BearerTokenVerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].

export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticate([Link])

@get('/users', {

responses: {

'200': {

description: 'Array of User model instances',

content: {

'application/json': {

schema: {type: 'array', items: {'x-ts-type': User}},

},

},

},
},

})

async find(

@[Link]('filter', getFilterSchemaFor(User)) filte


): Promise<User[]> {

return await [Link](filter);

}
For accessing the authenticated AuthUser model reference, you can inject the
CURRENT_USER provider, provided by the extension, which is populated by the auth
action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

local
First, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is a
simple example.

export class LocalPasswordVerifyProvider

implements Provider<[Link]> {

constructor(

@repository(UserRepository)

public userRepository: UserRepository,

) {}

value(): [Link] {

return async (username: any, password: any) => {

try {

const user: AuthUser = new AuthUser(

await [Link](username, pa
);

return user;

} catch (error) {

throw new [Link]([Link]


.message;

};
}

Please note the Verify function type [Link]

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].LOCAL_PASSWORD_VERIFIER).toProvid
LocalPasswordVerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].


export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticate([Link])

@post('/auth/login', {

responses: {

[STATUS_CODE.OK]: {

description: 'Auth Code',

content: {

[CONTENT_TYPE.JSON]: Object,

},

},

},
})

async login(

@requestBody()

req: LoginRequest,

): Promise<{

code: string;

}> {

......

For accessing the authenticated AuthUser model reference, you can inject the
CURRENT_USER provider, provided by the extension, which is populated by the auth
action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

Oauth2-resource-owner-password
First, create an AuthClient model implementing the IAuthClient interface. The purpose
of this model is to store oauth registered clients for the app in the DB. See sample
below.

@model({

name: 'auth_clients',

})

export class AuthClient extends Entity implements IAuthClient {


@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'client_id',

})

clientId: string;

@property({

type: 'string',

required: true,

name: 'client_secret',

})

clientSecret: string;

@property({

type: 'array',

itemType: 'number',

name: 'user_ids',

})

userIds: number[];

constructor(data?: Partial<AuthClient>) {

super(data);

Next, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

Create CRUD repository for both of the above models. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is a
simple example.

export class ResourceOwnerVerifyProvider

implements Provider<[Link]> {
constructor(

@repository(UserRepository)

public userRepository: UserRepository,

@repository(AuthClientRepository)

public authClientRepository: AuthClientRepository,

) {}

value(): [Link] {

return async (clientId, clientSecret, username, password) =


const user = await [Link](use
if (!user) {
throw new [Link]([Link]
}

const client = await [Link]({

where: {

clientId,

},

});

if (!client || [Link]([Link] || 0) < 0)


throw new [Link]([Link]
} else if (![Link] || [Link] !=
throw new [Link](

[Link],

);

return {

client,

user,

};

};
}

Please note the Verify function type [Link]. Also, in this


case, verifier will return AuthClient as well as User model.

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].RESOURCE_OWNER_PASSWORD_VERIFIER)
ResourceOwnerVerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].

export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticate(STRATEGY.OAUTH2_RESOURCE_OWNER_GRANT)

@post('/auth/login-token', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response Model',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async loginWithClientUser(

@requestBody() req: LoginRequest,

): Promise<TokenResponse> {

......

For accessing the authenticated AuthUser and AuthClient model reference, you can
inject the CURRENT_USER and CURRENT_CLIENT provider, provided by the
extension, which is populated by the auth action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

@[Link](AuthenticationBindings.CURRENT_CLIENT)

private readonly getCurrentClient: Getter<AuthClient>,

Google Oauth 2
First, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

// Auth provider - 'google'

@property({

type: 'string',

required: true,

name: 'auth_provider',

})

authProvider: string;

// Id from external provider

@property({

type: 'string',

name: 'auth_id',

})

authId?: string;

@property({

type: 'string',

name: 'auth_token',

})

authToken?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is a
simple example.

import {Provider} from '@loopback/context';

import {repository} from '@loopback/repository';

import {HttpErrors} from '@loopback/rest';

import {AuthErrorKeys, VerifyFunction} from 'loopback4-authenti

import {Tenant} from '../../../models';

import {UserCredentialsRepository, UserRepository} from '../../


import {AuthUser} from '../models/[Link]';

export class GoogleOauth2VerifyProvider

implements Provider<[Link]> {

constructor(

@repository(UserRepository)

public userRepository: UserRepository,

@repository(UserCredentialsRepository)

public userCredsRepository: UserCredentialsRepository,

) {}

value(): [Link] {

return async (accessToken, refreshToken, profile) => {

const user = await [Link]({

where: {

/* eslint-disable-next-line @typescript-eslint/no-exp
email: (profile as any)._json.email,

},

});

if (!user) {
throw new [Link]([Link]
}

if (

!user ||

[Link] !== 'google' ||

[Link] !== [Link]

) {

throw new [Link]([Link]


}

const authUser: AuthUser = new AuthUser(user);

[Link] = [];

[Link] = accessToken;

[Link] = refreshToken;

[Link] = new Tenant({id: [Link]});

return authUser;

};
}

}
Please note the Verify function type [Link]

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].GOOGLE_OAUTH2_VERIFIER).toProvide
GoogleOauth2VerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].

export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


request,

response,

);

const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticateClient(STRATEGY.CLIENT_PASSWORD)

@authenticate(

STRATEGY.GOOGLE_OAUTH2,

accessType: 'offline',

scope: ['profile', 'email'],

authorizationURL: [Link].GOOGLE_AUTH_URL,

callbackURL: [Link].GOOGLE_AUTH_CALLBACK_URL,

clientID: [Link].GOOGLE_AUTH_CLIENT_ID,

clientSecret: [Link].GOOGLE_AUTH_CLIENT_SECRET,

tokenURL: [Link].GOOGLE_AUTH_TOKEN_URL,

},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => key + '=' + [Link][key])

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/google', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async loginViaGoogle(

@[Link]('client_id')

clientId?: string,

@[Link]('client_secret')

clientSecret?: string,

): Promise<void> {}

@authenticate(

STRATEGY.GOOGLE_OAUTH2,

accessType: 'offline',

scope: ['profile', 'email'],

authorizationURL: [Link].GOOGLE_AUTH_URL,

callbackURL: [Link].GOOGLE_AUTH_CALLBACK_URL,

clientID: [Link].GOOGLE_AUTH_CLIENT_ID,

clientSecret: [Link].GOOGLE_AUTH_CLIENT_SECRET,

tokenURL: [Link].GOOGLE_AUTH_TOKEN_URL,

},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => `${key}=${[Link][key]}`)

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/google-auth-redirect', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async googleCallback(

@[Link]('code') code: string,

@[Link]('state') state: string,

@inject([Link]) response: Response,

): Promise<void> {

const clientId = new URLSearchParams(state).get('client_id'


if (!clientId || ![Link]) {

throw new [Link]([Link]


}

const client = await [Link]({

where: {

clientId: clientId,

},

});

if (!client || ![Link]) {

throw new [Link]([Link]


}

try {

const codePayload: ClientAuthCode<User> = {

clientId,

user: [Link],

};

const token = [Link](codePayload, [Link], {

expiresIn: [Link],

audience: clientId,

subject: [Link],

issuer: [Link].JWT_ISSUER,

});

[Link](`${[Link]}?code=${token}`);
} catch (error) {

throw new [Link]([Link]


}

Please note above that we are creating two new APIs for google auth. The first one is
for UI clients to hit. We are authenticating client as well, then passing the details to
the google auth. Then, the actual authentication is done by google authorization url,
which redirects to the second API we created after success. The first API method
body is empty as we do not need to handle its response. The google auth provider in
this package will do the redirection for you automatically.

For accessing the authenticated AuthUser model reference, you can inject the
CURRENT_USER provider, provided by the extension, which is populated by the auth
action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

Instagram Oauth 2
First, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

// Auth provider - 'instagram'

@property({

type: 'string',

required: true,

name: 'auth_provider',

})

authProvider: string;

// Id from external provider

@property({

type: 'string',

name: 'auth_id',

})

authId?: string;

@property({

type: 'string',

name: 'auth_token',

})

authToken?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is a
simple example.
import {Provider} from '@loopback/context';

import {repository} from '@loopback/repository';

import {HttpErrors} from '@loopback/rest';

import {AuthErrorKeys, VerifyFunction} from 'loopback4-authenti

import {Tenant} from '../../../models';

import {UserCredentialsRepository, UserRepository} from '../../


import {AuthUser} from '../models/[Link]';

export class InstagramOauth2VerifyProvider

implements Provider<[Link]> {

constructor(

@repository(UserRepository)

public userRepository: UserRepository,

@repository(UserCredentialsRepository)

public userCredsRepository: UserCredentialsRepository,

) {}

value(): [Link] {

return async (accessToken, refreshToken, profile) => {

const user = await [Link]({

where: {

/* eslint-disable-next-line @typescript-eslint/no-exp
email: (profile as any)._json.email,

},

});

if (!user) {
throw new [Link]([Link]
}

if (

!user ||

[Link] !== 'instagram' ||

[Link] !== [Link]

) {

throw new [Link]([Link]


}

const authUser: AuthUser = new AuthUser(user);

[Link] = [];

[Link] = accessToken;

[Link] = refreshToken;

[Link] = new Tenant({id: [Link]});

return authUser;

};
}

Please note the Verify function type [Link]

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].INSTAGRAM_OAUTH2_VERIFIER).toProv
InstagramOauth2VerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].

export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


request,

response,

);

const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticateClient(STRATEGY.CLIENT_PASSWORD)

@authenticate(

STRATEGY.INSTAGRAM_OAUTH2,

accessType: 'offline',

authorizationURL: [Link].INSTAGRAM_AUTH_URL,

callbackURL: [Link].INSTAGRAM_AUTH_CALLBACK_URL,

clientID: [Link].INSTAGRAM_AUTH_CLIENT_ID,

clientSecret: [Link].INSTAGRAM_AUTH_CLIENT_SECRET,

tokenURL: [Link].INSTAGRAM_AUTH_TOKEN_URL,

},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => key + '=' + [Link][key])

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/instagram', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async loginViaInstagram(

@[Link]('client_id')

clientId?: string,

@[Link]('client_secret')

clientSecret?: string,

): Promise<void> {}

@authenticate(

STRATEGY.INSTAGRAM_OAUTH2,

accessType: 'offline',

authorizationURL: [Link].INSTAGRAM_AUTH_URL,

callbackURL: [Link].INSTAGRAM_AUTH_CALLBACK_URL,

clientID: [Link].INSTAGRAM_AUTH_CLIENT_ID,

clientSecret: [Link].INSTAGRAM_AUTH_CLIENT_SECRET,

tokenURL: [Link].INSTAGRAM_AUTH_TOKEN_URL,

},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => `${key}=${[Link][key]}`)

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/instagram-auth-redirect', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async instagramCallback(

@[Link]('code') code: string,

@[Link]('state') state: string,

@inject([Link]) response: Response,

): Promise<void> {

const clientId = new URLSearchParams(state).get('client_id'


if (!clientId || ![Link]) {

throw new [Link]([Link]


}

const client = await [Link]({

where: {

clientId: clientId,

},

});

if (!client || ![Link]) {

throw new [Link]([Link]


}

try {

const codePayload: ClientAuthCode<User> = {

clientId,

user: [Link],

};

const token = [Link](codePayload, [Link], {

expiresIn: [Link],

audience: clientId,

subject: [Link],

issuer: [Link].JWT_ISSUER,

});

[Link](`${[Link]}?code=${token}`);
} catch (error) {

throw new [Link]([Link]


}

Please note above that we are creating two new APIs for instagram auth. The first
one is for UI clients to hit. We are authenticating client as well, then passing the
details to the instagram auth. Then, the actual authentication is done by instagram
authorization url, which redirects to the second API we created after success. The first
API method body is empty as we do not need to handle its response. The instagram
auth provider in this package will do the redirection for you automatically.

For accessing the authenticated AuthUser model reference, you can inject the
CURRENT_USER provider, provided by the extension, which is populated by the auth
action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

Apple Oauth 2
First, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

// Auth provider - 'apple'

@property({

type: 'string',

required: true,

name: 'auth_provider',

})

authProvider: string;

// Id from external provider

@property({

type: 'string',

name: 'auth_id',

})

authId?: string;

@property({

type: 'string',

name: 'auth_token',

})

authToken?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

}
Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is a
simple example.

import {Provider} from '@loopback/context';

import {repository} from '@loopback/repository';

import {HttpErrors} from '@loopback/rest';

import {AuthErrorKeys, VerifyFunction} from 'loopback4-authenti

import {Tenant} from '../../../models';

import {UserCredentialsRepository, UserRepository} from '../../


import {AuthUser} from '../models/[Link]';

export class AppleOauth2VerifyProvider

implements Provider<[Link]> {

constructor(

@repository(UserRepository)

public userRepository: UserRepository,

@repository(UserCredentialsRepository)

public userCredsRepository: UserCredentialsRepository,

) {}

value(): [Link] {

return async (accessToken, refreshToken, profile) => {

const user = await [Link]({

where: {

/* eslint-disable-next-line @typescript-eslint/no-exp
email: (profile as any)._json.email,

},

});

if (!user) {
throw new [Link]([Link]
}

if (

!user ||

[Link] !== 'apple' ||

[Link] !== [Link]

) {

throw new [Link]([Link]


}

const authUser: AuthUser = new AuthUser(user);

[Link] = [];

[Link] = accessToken;

[Link] = refreshToken;

[Link] = new Tenant({id: [Link]});

return authUser;

};
}

Please note the Verify function type [Link]

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].APPLE_OAUTH2_VERIFIER).toProvider
AppleOauth2VerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].


export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


request,

response,

);

const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticateClient(STRATEGY.CLIENT_PASSWORD)

@authenticate(

STRATEGY.APPLE_OAUTH2,

accessType: 'offline',

scope: ['name', 'email'],

callbackURL: [Link].APPLE_AUTH_CALLBACK_URL,

clientID: [Link].APPLE_AUTH_CLIENT_ID,

teamID: [Link].APPLE_AUTH_TEAM_ID,

keyID: [Link].APPLE_AUTH_KEY_ID,

privateKeyLocation: [Link].APPLE_AUTH_PRIVATE_KEY_LO
},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => key + '=' + [Link][key])

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/oauth-apple', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async loginViaApple(

@[Link]('client_id')

clientId?: string,

@[Link]('client_secret')

clientSecret?: string,

): Promise<void> {}

@authenticate(

STRATEGY.APPLE_OAUTH2,

accessType: 'offline',

scope: ['name', 'email'],

callbackURL: [Link].APPLE_AUTH_CALLBACK_URL,

clientID: [Link].APPLE_AUTH_CLIENT_ID,

teamID: [Link].APPLE_AUTH_TEAM_ID,

keyID: [Link].APPLE_AUTH_KEY_ID,

privateKeyLocation: [Link].APPLE_AUTH_PRIVATE_KEY_LO
},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => `${key}=${[Link][key]}`)

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/apple-oauth-redirect', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async appleCallback(

@[Link]('code') code: string,

@[Link]('state') state: string,

@inject([Link]) response: Response,

): Promise<void> {

const clientId = new URLSearchParams(state).get('client_id'


if (!clientId || ![Link]) {

throw new [Link]([Link]


}

const client = await [Link]({

where: {

clientId: clientId,

},

});

if (!client || ![Link]) {

throw new [Link]([Link]


}

try {

const codePayload: ClientAuthCode<User> = {

clientId,

user: [Link],

};

const token = [Link](codePayload, [Link], {

expiresIn: [Link],

audience: clientId,

subject: [Link],

issuer: [Link].JWT_ISSUER,

});

[Link](`${[Link]}?code=${token}`);
} catch (error) {

throw new [Link]([Link]


}

Please note above that we are creating two new APIs for apple auth. The first one is
for UI clients to hit. We are authenticating client as well, then passing the details to
the apple auth. Then, the actual authentication is done by apple authorization url,
which redirects to the second API we created after success. The first API method
body is empty as we do not need to handle its response. The apple auth provider in
this package will do the redirection for you automatically.

For accessing the authenticated AuthUser model reference, you can inject the
CURRENT_USER provider, provided by the extension, which is populated by the auth
action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

Facebook Oauth 2
First, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

// Auth provider - 'facebook'

@property({

type: 'string',

required: true,

name: 'auth_provider',

})

authProvider: string;

// Id from external provider

@property({

type: 'string',

name: 'auth_id',

})

authId?: string;

@property({

type: 'string',

name: 'auth_token',

})

authToken?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is a
simple example.

import {Provider} from '@loopback/context';

import {repository} from '@loopback/repository';

import {HttpErrors} from '@loopback/rest';

import {AuthErrorKeys, VerifyFunction} from 'loopback4-authenti

import {Tenant} from '../../../models';

import {UserCredentialsRepository, UserRepository} from '../../


import {AuthUser} from '../models/[Link]';

export class FacebookOauth2VerifyProvider

implements Provider<[Link]> {

constructor(

@repository(UserRepository)

public userRepository: UserRepository,

@repository(UserCredentialsRepository)

public userCredsRepository: UserCredentialsRepository,

) {}

value(): [Link] {

return async (accessToken, refreshToken, profile) => {

const user = await [Link]({

where: {

/* eslint-disable-next-line @typescript-eslint/no-exp
email: (profile as any)._json.email,

},

});

if (!user) {
throw new [Link]([Link]
}

if (

!user ||

[Link] !== 'facebook' ||

[Link] !== [Link]

) {

throw new [Link]([Link]


}

const authUser: AuthUser = new AuthUser(user);

[Link] = [];

[Link] = accessToken;

[Link] = refreshToken;

[Link] = new Tenant({id: [Link]});

return authUser;

};
}

Please note the Verify function type [Link]

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au


// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].FACEBOOK_OAUTH2_VERIFIER).toProvi
FacebookOauth2VerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].

export class MySequence implements SequenceHandler {

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


request,

response,

);

const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticateClient(STRATEGY.CLIENT_PASSWORD)

@authenticate(

STRATEGY.FACEBOOK_OAUTH2,

accessType: 'offline',

authorizationURL: [Link].FACEBOOK_AUTH_URL,

callbackURL: [Link].FACEBOOK_AUTH_CALLBACK_URL,

clientID: [Link].FACEBOOK_AUTH_CLIENT_ID,

clientSecret: [Link].FACEBOOK_AUTH_CLIENT_SECRET,

tokenURL: [Link].FACEBOOK_AUTH_TOKEN_URL,

},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => key + '=' + [Link][key])

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/facebook', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async loginViaFacebook(

@[Link]('client_id')

clientId?: string,

@[Link]('client_secret')

clientSecret?: string,

): Promise<void> {}

@authenticate(

STRATEGY.FACEBOOK_OAUTH2,

accessType: 'offline',

authorizationURL: [Link].FACEBOOK_AUTH_URL,

callbackURL: [Link].FACEBOOK_AUTH_CALLBACK_URL,

clientID: [Link].FACEBOOK_AUTH_CLIENT_ID,

clientSecret: [Link].FACEBOOK_AUTH_CLIENT_SECRET,

tokenURL: [Link].FACEBOOK_AUTH_TOKEN_URL,

},
(req: Request) => {

return {

accessType: 'offline',

state: [Link]([Link])

.map(key => `${key}=${[Link][key]}`)

.join('&'),

};

},
)

@authorize(['*'])

@get('/auth/facebook-auth-redirect', {

responses: {

[STATUS_CODE.OK]: {

description: 'Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async facebookCallback(

@[Link]('code') code: string,

@[Link]('state') state: string,

@inject([Link]) response: Response,

): Promise<void> {

const clientId = new URLSearchParams(state).get('client_id'


if (!clientId || ![Link]) {

throw new [Link]([Link]


}

const client = await [Link]({

where: {

clientId: clientId,

},

});

if (!client || ![Link]) {

throw new [Link]([Link]


}

try {

const codePayload: ClientAuthCode<User> = {

clientId,

user: [Link],

};

const token = [Link](codePayload, [Link], {

expiresIn: [Link],

audience: clientId,

subject: [Link],

issuer: [Link].JWT_ISSUER,

});

[Link](`${[Link]}?code=${token}`);
} catch (error) {

throw new [Link]([Link]


}

Please note above that we are creating two new APIs for facebook auth. The first one
is for UI clients to hit. We are authenticating client as well, then passing the details to
the facebook auth. Then, the actual authentication is done by facebook authorization
url, which redirects to the second API we created after success. The first API method
body is empty as we do not need to handle its response. The facebook auth provider
in this package will do the redirection for you automatically.

For accessing the authenticated AuthUser model reference, you can inject the
CURRENT_USER provider, provided by the extension, which is populated by the auth
action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

Keycloak
First, create a AuthUser model implementing the IAuthUser interface. You can
implement the interface in the user model itself. See sample below.

@model({

name: 'users',

})

export class User extends Entity implements IAuthUser {

@property({

type: 'number',

id: true,

})

id?: number;

@property({

type: 'string',

required: true,

name: 'first_name',

})

firstName: string;

@property({

type: 'string',

name: 'last_name',

})

lastName: string;

@property({

type: 'string',

name: 'middle_name',

})

middleName?: string;

@property({

type: 'string',

required: true,

})

username: string;

@property({

type: 'string',

})

email?: string;

// Auth provider - 'keycloak'

@property({

type: 'string',

required: true,

name: 'auth_provider',

})

authProvider: string;

// Id from external provider

@property({

type: 'string',

name: 'auth_id',

})

authId?: string;

@property({

type: 'string',

name: 'auth_token',

})

authToken?: string;

@property({

type: 'string',

})

password?: string;

constructor(data?: Partial<User>) {

super(data);

Create CRUD repository for the above model. Use loopback CLI.

lb4 repository

Add the verifier function for the strategy. You need to create a provider for the same.
You can add your application specific business logic for client auth here. Here is a
simple example.

import {Provider, inject} from '@loopback/context';

import {repository} from '@loopback/repository';

import {HttpErrors} from '@loopback/rest';

import {

AuthErrorKeys,

IAuthUser,

VerifyFunction,

} from 'loopback4-authentication';

import {UserCredentialsRepository, UserRepository} from '../../


import {AuthUser} from '../models/[Link]';

export class KeycloakVerifyProvider

implements Provider<[Link]> {

constructor(

@repository(UserRepository)

public userRepository: UserRepository,

@repository(UserCredentialsRepository)

public userCredsRepository: UserCredentialsRepository,

) {}

value(): [Link] {

return async (accessToken, refreshToken, profile) => {

let user: IAuthUser | null = await [Link]


where: {

email: [Link],

},

});

if (!user) {
throw new [Link]([Link]
}

const creds = await [Link]({

where: {

userId: [Link] as string,

},

});

if (

!creds ||

[Link] !== 'keycloak' ||

[Link] !== [Link]

) {

throw new [Link]([Link]


}

const authUser: AuthUser = new AuthUser({

...user,

id: [Link] as string,

});

[Link] = [];

[Link] = accessToken;

[Link] = refreshToken;

return authUser;

};
}

Please note the Verify function type [Link]

Now bind this provider to the application in [Link].

import {AuthenticationComponent, Strategies} from 'loopback4-au

// Add authentication component

[Link](AuthenticationComponent);

// Customize authentication verify handlers

[Link]([Link].KEYCLOAK_VERIFIER).toProvider(

KeycloakVerifyProvider,

);

Finally, add the authenticate function as a sequence action to [Link].

export class MySequence implements SequenceHandler {

/**

* Optional invoker for registered middleware in a chain.

* To be injected via SequenceActions.INVOKE_MIDDLEWARE.

*/

@inject(SequenceActions.INVOKE_MIDDLEWARE, {optional: true})

protected invokeMiddleware: InvokeMiddleware = () => false;

constructor(

@inject(SequenceActions.FIND_ROUTE) protected findRoute: Fi


@inject(SequenceActions.PARSE_PARAMS) protected parseParams
@inject(SequenceActions.INVOKE_METHOD) protected invoke: In
@inject([Link]) public send: Send,

@inject([Link]) public reject: Reject,

@inject(AuthenticationBindings.USER_AUTH_ACTION)

protected authenticateRequest: AuthenticateFn<AuthUser>,

) {}

async handle(context: RequestContext) {

try {

const {request, response} = context;

const route = [Link](request);

const args = await [Link](request, route);

[Link] = args[[Link] - 1];

const authUser: AuthUser = await [Link]


request,

response,

);

const result = await [Link](route, args);

[Link](response, result);

} catch (err) {

[Link](context, err);

After this, you can use decorator to apply auth to controller functions wherever
needed. See below.

@authenticateClient(STRATEGY.CLIENT_PASSWORD)

@authenticate(

[Link],

host: [Link].KEYCLOAK_HOST,

realm: [Link].KEYCLOAK_REALM, //'Tenant1',

clientID: [Link].KEYCLOAK_CLIENT_ID, //'onboarding',


clientSecret: [Link].KEYCLOAK_CLIENT_SECRET, //'e607
callbackURL: [Link].KEYCLOAK_CALLBACK_URL, //'http:/
authorizationURL: `${[Link].KEYCLOAK_HOST}/auth/real
tokenURL: `${[Link].KEYCLOAK_HOST}/auth/realms/${pro
userInfoURL: `${[Link].KEYCLOAK_HOST}/auth/realms/${
},
keycloakQueryGen,

@authorize({permissions: ['*']})

@get('/auth/keycloak', {

responses: {

[STATUS_CODE.OK]: {

description: 'Keycloak Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async loginViaKeycloak(

@[Link]('client_id')

clientId?: string,

@[Link]('client_secret')

clientSecret?: string,

): Promise<void> {}

@authenticate(

[Link],

host: [Link].KEYCLOAK_HOST,

realm: [Link].KEYCLOAK_REALM,

clientID: [Link].KEYCLOAK_CLIENT_ID,

clientSecret: [Link].KEYCLOAK_CLIENT_SECRET,

callbackURL: [Link].KEYCLOAK_CALLBACK_URL,

authorizationURL: `${[Link].KEYCLOAK_HOST}/auth/real
tokenURL: `${[Link].KEYCLOAK_HOST}/auth/realms/${pro
userInfoURL: `${[Link].KEYCLOAK_HOST}/auth/realms/${
},
keycloakQueryGen,

@authorize({permissions: ['*']})

@get('/auth/keycloak-auth-redirect', {

responses: {

[STATUS_CODE.OK]: {

description: 'Keycloak Redirect Token Response',

content: {

[CONTENT_TYPE.JSON]: {

schema: {'x-ts-type': TokenResponse},

},

},

},

},
})

async keycloakCallback(

@[Link]('code') code: string,

@[Link]('state') state: string,

@inject([Link]) response: Response,

): Promise<void> {

const clientId = new URLSearchParams(state).get('client_id'


if (!clientId || ![Link]) {

throw new [Link]([Link]


}

const client = await [Link]({

where: {

clientId,

},

});

if (!client || ![Link]) {

throw new [Link]([Link]


}

try {

const codePayload: ClientAuthCode<User, typeof [Link]


clientId,

user: [Link],

};

const token = [Link](codePayload, [Link], {

expiresIn: [Link],

audience: clientId,

subject: [Link],

issuer: [Link].JWT_ISSUER,

});

[Link](

`${[Link]}?code=${token}&user=${[Link].u
);

} catch (error) {

[Link](error);

throw new [Link]([Link]


}

Please note above that we are creating two new APIs for keycloak auth. The first one
is for UI clients to hit. We are authenticating client as well, then passing the details to
the keycloak auth. Then, the actual authentication is done by keycloak authorization
url, which redirects to the second API we created after success. The first API method
body is empty as we do not need to handle its response. The keycloak auth provider
in this package will do the redirection for you automatically.

For accessing the authenticated AuthUser model reference, you can inject the
CURRENT_USER provider, provided by the extension, which is populated by the auth
action sequence above.

@[Link](AuthenticationBindings.CURRENT_USER)

private readonly getCurrentUser: Getter<User>,

Custom Verifier for Individual Routes


For providing a custom verifier for a particular route, you can pass a binding key for a
verifier provider as the fourth parameter of the authenticate decorator. Note - The key
VerifyBindings.BEARER_SIGNUP_VERIFY_PROVIDER can be any custom key, it
just be bound to a verify function provider.

@authenticate(

[Link],

undefined, //options

undefined, //authOptions

VerifyBindings.BEARER_SIGNUP_VERIFY_PROVIDER,

And binding this key to a verifier in the [Link]

[Link](VerifyBindings.BEARER_SIGNUP_VERIFY_PROVIDER).toProvi
LocalPreSignupProvider as Constructor<Provider<PreSignupFn>>,
);

Https proxy support for keycloak and google auth


If a https proxy agent is needed for keycloak and google auth, just add an
environment variable named  HTTPS_PROXY  or  https_proxy  with proxy url as value.
It will add that proxy agent to the request.

Feedback
If you've noticed a bug or have a question or have a feature request, search the
issue tracker to see if someone else in the community has already created a ticket. If
not, go ahead and make one! All feature requests are welcome. Implementation time
may vary. Feel free to contribute the same, if you can. If you think this extension is
useful, please star it. Appreciation really helps in keeping this project alive.

Contributing
Please read [Link] for details on the process for submitting pull
requests to us.

Code of conduct
Code of conduct guidelines here.

License
MIT

You might also like