Skip to content

Commit c2f91ad

Browse files
committed
Added keycloak startegy
1 parent a9988b2 commit c2f91ad

File tree

9 files changed

+184
-63
lines changed

9 files changed

+184
-63
lines changed

package-lock.json

Lines changed: 65 additions & 56 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@
4646
],
4747
"peerDependencies": {
4848
"@loopback/boot": "^2.1.2",
49-
"@loopback/context": "^3.6.0",
50-
"@loopback/core": "^2.4.2",
5149
"@loopback/rest": "^3.3.2"
5250
},
5351
"dependencies": {
54-
"@loopback/core": "^2.7.0",
52+
"@exlinc/keycloak-passport": "^1.0.2",
53+
"@loopback/context": "^3.9.2",
54+
"@loopback/core": "^2.9.1",
5555
"passport": "^0.4.1",
5656
"passport-azure-ad": "^4.2.1",
5757
"passport-google-oauth20": "^2.0.0",
@@ -63,7 +63,6 @@
6363
"@istanbuljs/nyc-config-typescript": "^1.0.1",
6464
"@loopback/boot": "^2.3.1",
6565
"@loopback/build": "^5.4.1",
66-
"@loopback/context": "^3.8.1",
6766
"@loopback/metadata": "^2.1.5",
6867
"@loopback/rest": "^5.0.1",
6968
"@loopback/testlab": "^3.1.5",
@@ -77,7 +76,7 @@
7776
"@types/passport-http-bearer": "^1.0.35",
7877
"@types/passport-local": "^1.0.33",
7978
"@types/passport-oauth2-client-password": "^0.1.2",
80-
"lodash": "^4.17.15",
79+
"lodash": "^4.17.19",
8180
"nyc": "^15.0.1",
8281
"source-map-support": "^0.5.19",
8382
"ts-node": "^8.10.1",

src/strategies/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './client-auth-strategy.provider';
22
export * from './user-auth-strategy.provider';
33
export * from './passport';
4+
export * from './types';

src/strategies/keys.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import {BearerStrategyFactory} from './passport/passport-bearer';
44
import {ResourceOwnerPasswordStrategyFactory} from './passport/passport-resource-owner-password';
55
import {ClientPasswordStrategyFactory} from './passport/passport-client-password/client-password-strategy-factory-provider';
66
import {GoogleAuthStrategyFactoryProvider} from './passport/passport-google-oauth2';
7-
import {VerifyFunction} from './passport';
7+
import {KeycloakStrategyFactoryProvider} from './passport/passport-keycloak';
88
import {AzureADAuthStrategyFactoryProvider} from './passport/passport-azure-ad';
9+
import {VerifyFunction} from './types';
910

1011
export namespace Strategies {
1112
export namespace Passport {
@@ -55,5 +56,13 @@ export namespace Strategies {
5556
export const AZURE_AD_VERIFIER = BindingKey.create<
5657
VerifyFunction.AzureADAuthFn
5758
>('sf.passport.verifier.azureAd');
59+
60+
// Passport-keycloak strategy
61+
export const KEYCLOAK_STRATEGY_FACTORY = BindingKey.create<
62+
KeycloakStrategyFactoryProvider
63+
>('sf.passport.strategyFactory.keycloak');
64+
export const KEYCLOAK_VERIFIER = BindingKey.create<
65+
VerifyFunction.KeycloakAuthFn
66+
>('sf.passport.verifier.keycloak');
5867
}
5968
}

src/strategies/passport/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ export * from './passport-bearer';
22
export * from './passport-client-password';
33
export * from './passport-local';
44
export * from './passport-resource-owner-password';
5-
export * from '../types';
5+
export * from './passport-keycloak';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './keycloak-strategy-factory-provider';
2+
export * from './keycloak-verify.provider';
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {inject, Provider} from '@loopback/core';
2+
import {HttpErrors} from '@loopback/rest';
3+
4+
import {AuthErrorKeys} from '../../../error-keys';
5+
import {IAuthUser} from '../../../types';
6+
import {Strategies} from '../../keys';
7+
import {KeycloakProfile, VerifyFunction} from '../../types';
8+
9+
export const KeycloakStrategy = require('@exlinc/keycloak-passport');
10+
11+
export interface KeycloakStrategyFactory {
12+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
13+
(options: any): typeof KeycloakStrategy;
14+
}
15+
16+
export class KeycloakStrategyFactoryProvider
17+
implements Provider<KeycloakStrategyFactory> {
18+
constructor(
19+
@inject(Strategies.Passport.KEYCLOAK_VERIFIER)
20+
private readonly verifierKeycloak: VerifyFunction.KeycloakAuthFn,
21+
) {}
22+
23+
value(): KeycloakStrategyFactory {
24+
return (options) => this.getGoogleAuthStrategyVerifier(options);
25+
}
26+
27+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
28+
getGoogleAuthStrategyVerifier(options: any): typeof KeycloakStrategy {
29+
return new KeycloakStrategy(
30+
options,
31+
async (
32+
accessToken: string,
33+
refreshToken: string,
34+
profile: KeycloakProfile,
35+
cb: (err?: string | Error, user?: IAuthUser) => void,
36+
) => {
37+
try {
38+
const user = await this.verifierKeycloak(
39+
accessToken,
40+
refreshToken,
41+
profile,
42+
cb,
43+
);
44+
if (!user) {
45+
throw new HttpErrors.Unauthorized(AuthErrorKeys.InvalidCredentials);
46+
}
47+
cb(undefined, user);
48+
} catch (err) {
49+
cb(err);
50+
}
51+
},
52+
);
53+
}
54+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {Provider} from '@loopback/context';
2+
import {HttpErrors} from '@loopback/rest';
3+
4+
import {KeycloakProfile, VerifyFunction} from '../../types';
5+
6+
/**
7+
* A provider for default implementation of VerifyFunction.LocalPasswordFn
8+
*
9+
* It will just throw an error saying Not Implemented
10+
*/
11+
export class KeycloakVerifyProvider
12+
implements Provider<VerifyFunction.KeycloakAuthFn> {
13+
constructor() {}
14+
15+
value(): VerifyFunction.KeycloakAuthFn {
16+
return async (
17+
accessToken: string,
18+
refreshToken: string,
19+
profile: KeycloakProfile,
20+
cb: (err?: string | Error, user?: KeycloakProfile) => void,
21+
) => {
22+
throw new HttpErrors.NotImplemented(
23+
`VerifyFunction.KeycloakAuthFn is not implemented`,
24+
);
25+
};
26+
}
27+
}

src/strategies/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,24 @@ export namespace VerifyFunction {
5252
req?: Request,
5353
): Promise<IAuthUser | null>;
5454
}
55+
56+
export interface KeycloakAuthFn {
57+
(
58+
accessToken: string,
59+
refreshToken: string,
60+
profile: KeycloakProfile,
61+
cb: (err?: string | Error, user?: IAuthUser) => void,
62+
): Promise<IAuthUser | null>;
63+
}
64+
}
65+
66+
export interface KeycloakProfile {
67+
keycloakId: string;
68+
fullName: string;
69+
firstName: string;
70+
lastName: string;
71+
username: string;
72+
email: string;
73+
avatar: string;
74+
realm: string;
5575
}

0 commit comments

Comments
 (0)