Skip to content

Commit 0691f35

Browse files
feat(loopback4-authentication): facebook-oauth2 (#47)
1 parent cead435 commit 0691f35

File tree

12 files changed

+823
-2
lines changed

12 files changed

+823
-2
lines changed

README.md

Lines changed: 615 additions & 2 deletions
Large diffs are not rendered by default.

package-lock.json

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

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"passport": "^0.4.1",
5757
"passport-apple": "^2.0.1",
5858
"passport-azure-ad": "^4.3.0",
59+
"passport-facebook": "^3.0.0",
5960
"passport-google-oauth20": "^2.0.0",
6061
"passport-http-bearer": "^1.0.1",
6162
"passport-instagram": "^1.0.0",
@@ -76,6 +77,7 @@
7677
"@types/passport": "^1.0.3",
7778
"@types/passport-apple": "^1.1.0",
7879
"@types/passport-azure-ad": "^4.0.8",
80+
"@types/passport-facebook": "^2.1.10",
7981
"@types/passport-google-oauth20": "^2.0.3",
8082
"@types/passport-http-bearer": "^1.0.35",
8183
"@types/passport-instagram": "^1.0.1",

src/component.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
GoogleAuthVerifyProvider,
2121
InstagramAuthStrategyFactoryProvider,
2222
InstagramAuthVerifyProvider,
23+
FacebookAuthStrategyFactoryProvider,
24+
FacebookAuthVerifyProvider,
2325
AppleAuthStrategyFactoryProvider,
2426
AppleAuthVerifyProvider,
2527
KeycloakStrategyFactoryProvider,
@@ -55,6 +57,8 @@ export class AuthenticationComponent implements Component {
5557
.key]: GoogleAuthStrategyFactoryProvider,
5658
[Strategies.Passport.INSTAGRAM_OAUTH2_STRATEGY_FACTORY
5759
.key]: InstagramAuthStrategyFactoryProvider,
60+
[Strategies.Passport.FACEBOOK_OAUTH2_STRATEGY_FACTORY
61+
.key]: FacebookAuthStrategyFactoryProvider,
5862
[Strategies.Passport.APPLE_OAUTH2_STRATEGY_FACTORY
5963
.key]: AppleAuthStrategyFactoryProvider,
6064
[Strategies.Passport.AZURE_AD_STRATEGY_FACTORY
@@ -75,6 +79,8 @@ export class AuthenticationComponent implements Component {
7579
.key]: GoogleAuthVerifyProvider,
7680
[Strategies.Passport.INSTAGRAM_OAUTH2_VERIFIER
7781
.key]: InstagramAuthVerifyProvider,
82+
[Strategies.Passport.FACEBOOK_OAUTH2_VERIFIER
83+
.key]: FacebookAuthVerifyProvider,
7884
[Strategies.Passport.APPLE_OAUTH2_VERIFIER.key]: AppleAuthVerifyProvider,
7985
[Strategies.Passport.AZURE_AD_VERIFIER.key]: AzureADAuthVerifyProvider,
8086
[Strategies.Passport.KEYCLOAK_VERIFIER.key]: KeycloakVerifyProvider,

src/strategies/keys.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {AzureADAuthStrategyFactoryProvider} from './passport/passport-azure-ad';
99
import {VerifyFunction} from './types';
1010
import {InstagramAuthStrategyFactoryProvider} from './passport';
1111
import {AppleAuthStrategyFactoryProvider} from './passport/passport-apple-oauth2';
12+
import {FacebookAuthStrategyFactoryProvider} from './passport/passport-facebook-oauth2';
1213

1314
export namespace Strategies {
1415
export namespace Passport {
@@ -75,6 +76,14 @@ export namespace Strategies {
7576
'sf.passport.verifier.instagramOauth2',
7677
);
7778

79+
// Passport-facebook startegy
80+
export const FACEBOOK_OAUTH2_STRATEGY_FACTORY = BindingKey.create<FacebookAuthStrategyFactoryProvider>(
81+
'sf.passport.strategyFactory.facebookOauth2',
82+
);
83+
export const FACEBOOK_OAUTH2_VERIFIER = BindingKey.create<VerifyFunction.FacebookAuthFn>(
84+
'sf.passport.verifier.facebookOauth2',
85+
);
86+
7887
// Passport-apple-oauth2 strategy
7988
export const APPLE_OAUTH2_STRATEGY_FACTORY = BindingKey.create<AppleAuthStrategyFactoryProvider>(
8089
'sf.passport.strategyFactory.appleOauth2',

src/strategies/passport/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export * from './passport-google-oauth2';
77
export * from './passport-azure-ad';
88
export * from './passport-insta-oauth2';
99
export * from './passport-apple-oauth2';
10+
export * from './passport-facebook-oauth2';
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import {inject, Provider} from '@loopback/core';
2+
import {HttpErrors, Request} from '@loopback/rest';
3+
import {HttpsProxyAgent} from 'https-proxy-agent';
4+
import {
5+
Profile,
6+
Strategy,
7+
StrategyOption,
8+
StrategyOptionWithRequest,
9+
} from 'passport-facebook';
10+
11+
import {AuthErrorKeys} from '../../../error-keys';
12+
import {Strategies} from '../../keys';
13+
import {VerifyCallback, VerifyFunction} from '../../types';
14+
15+
interface ExtendedStrategyOption extends StrategyOption {
16+
passReqToCallback?: false;
17+
}
18+
19+
export interface FacebookAuthStrategyFactory {
20+
(
21+
options: ExtendedStrategyOption | StrategyOptionWithRequest,
22+
verifierPassed?: VerifyFunction.FacebookAuthFn,
23+
): Strategy;
24+
}
25+
26+
export class FacebookAuthStrategyFactoryProvider
27+
implements Provider<FacebookAuthStrategyFactory> {
28+
constructor(
29+
@inject(Strategies.Passport.FACEBOOK_OAUTH2_VERIFIER)
30+
private readonly verifierFacebookAuth: VerifyFunction.FacebookAuthFn,
31+
) {}
32+
33+
value(): FacebookAuthStrategyFactory {
34+
return (options, verifier) =>
35+
this.getFacebookAuthStrategyVerifier(options, verifier);
36+
}
37+
38+
getFacebookAuthStrategyVerifier(
39+
options: ExtendedStrategyOption | StrategyOptionWithRequest,
40+
verifierPassed?: VerifyFunction.FacebookAuthFn,
41+
): Strategy {
42+
const verifyFn = verifierPassed ?? this.verifierFacebookAuth;
43+
let strategy;
44+
if (options && options.passReqToCallback === true) {
45+
strategy = new Strategy(
46+
options,
47+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
48+
async (
49+
req: Request,
50+
accessToken: string,
51+
refreshToken: string,
52+
profile: Profile,
53+
cb: VerifyCallback,
54+
) => {
55+
try {
56+
const user = await verifyFn(
57+
accessToken,
58+
refreshToken,
59+
profile,
60+
cb,
61+
req,
62+
);
63+
if (!user) {
64+
throw new HttpErrors.Unauthorized(
65+
AuthErrorKeys.InvalidCredentials,
66+
);
67+
}
68+
cb(undefined, user);
69+
} catch (err) {
70+
cb(err);
71+
}
72+
},
73+
);
74+
} else {
75+
strategy = new Strategy(
76+
options as ExtendedStrategyOption,
77+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
78+
async (
79+
accessToken: string,
80+
refreshToken: string,
81+
profile: Profile,
82+
cb: VerifyCallback,
83+
) => {
84+
try {
85+
const user = await verifyFn(accessToken, refreshToken, profile, cb);
86+
if (!user) {
87+
throw new HttpErrors.Unauthorized(
88+
AuthErrorKeys.InvalidCredentials,
89+
);
90+
}
91+
cb(undefined, user);
92+
} catch (err) {
93+
cb(err);
94+
}
95+
},
96+
);
97+
}
98+
99+
this._setupProxy(strategy);
100+
return strategy;
101+
}
102+
103+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
104+
private _setupProxy(strategy: any) {
105+
// Setup proxy if any
106+
let httpsProxyAgent;
107+
if (process.env['https_proxy']) {
108+
httpsProxyAgent = new HttpsProxyAgent(process.env['https_proxy']);
109+
strategy._oauth2.setAgent(httpsProxyAgent);
110+
} else if (process.env['HTTPS_PROXY']) {
111+
httpsProxyAgent = new HttpsProxyAgent(process.env['HTTPS_PROXY']);
112+
strategy._oauth2.setAgent(httpsProxyAgent);
113+
}
114+
}
115+
}
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, Request} from '@loopback/rest';
3+
import * as FacebookStrategy from 'passport-facebook';
4+
import {VerifyCallback, VerifyFunction} from '../../types';
5+
/**
6+
* A provider for default implementation of VerifyFunction.LocalPasswordFn
7+
*
8+
* It will just throw an error saying Not Implemented
9+
*/
10+
export class FacebookAuthVerifyProvider
11+
implements Provider<VerifyFunction.FacebookAuthFn> {
12+
constructor() {}
13+
14+
value(): VerifyFunction.FacebookAuthFn {
15+
return async (
16+
accessToken: string,
17+
refreshToken: string,
18+
profile: FacebookStrategy.Profile,
19+
cb: VerifyCallback,
20+
req?: Request,
21+
) => {
22+
throw new HttpErrors.NotImplemented(
23+
`VerifyFunction.FacebookAuthFn is not implemented`,
24+
);
25+
};
26+
}
27+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './facebook-auth-strategy-factory-provider';
2+
export * from './facebook-auth-verify.provider';

src/strategies/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {Request} from '@loopback/rest';
22
import * as GoogleStrategy from 'passport-google-oauth20';
33
import * as AzureADStrategy from 'passport-azure-ad';
44
import * as InstagramStrategy from 'passport-instagram';
5+
import * as FacebookStrategy from 'passport-facebook';
56
import * as AppleStrategy from 'passport-apple';
67
import {DecodedIdToken} from 'passport-apple';
78
import {IAuthClient, IAuthUser} from '../types';
@@ -75,6 +76,17 @@ export namespace VerifyFunction {
7576
): Promise<T | null>;
7677
}
7778

79+
export interface FacebookAuthFn<T = IAuthUser>
80+
extends VerifyFunction.GenericAuthFn<T> {
81+
(
82+
accessToken: string,
83+
refreshToken: string,
84+
profile: FacebookStrategy.Profile,
85+
cb: VerifyCallback,
86+
req?: Request,
87+
): Promise<T | null>;
88+
}
89+
7890
export interface AppleAuthFn<T = IAuthUser> extends GenericAuthFn<T> {
7991
(
8092
accessToken: string,

0 commit comments

Comments
 (0)