Skip to content

Commit b515761

Browse files
Apple authentication added (#44)
1 parent e46998e commit b515761

File tree

14 files changed

+352
-0
lines changed

14 files changed

+352
-0
lines changed

.eslintrc.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
declare const _extends: string;
2+
export {_extends as extends};
3+
export declare const rules: {
4+
'no-extra-boolean-cast': string;
5+
'@typescript-eslint/interface-name-prefix': string;
6+
};
7+
export declare namespace parserOptions {
8+
const project: string;
9+
const tsconfigRootDir: string;
10+
}

package-lock.json

Lines changed: 79 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
@@ -54,6 +54,7 @@
5454
"@loopback/core": "^2.16.0",
5555
"https-proxy-agent": "^5.0.0",
5656
"passport": "^0.4.1",
57+
"passport-apple": "^2.0.1",
5758
"passport-azure-ad": "^4.3.0",
5859
"passport-google-oauth20": "^2.0.0",
5960
"passport-http-bearer": "^1.0.1",
@@ -73,6 +74,7 @@
7374
"@types/lodash": "^4.14.150",
7475
"@types/node": "^10.17.59",
7576
"@types/passport": "^1.0.3",
77+
"@types/passport-apple": "^1.1.0",
7678
"@types/passport-azure-ad": "^4.0.8",
7779
"@types/passport-google-oauth20": "^2.0.3",
7880
"@types/passport-http-bearer": "^1.0.35",

src/component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
GoogleAuthVerifyProvider,
2121
InstagramAuthStrategyFactoryProvider,
2222
InstagramAuthVerifyProvider,
23+
AppleAuthStrategyFactoryProvider,
24+
AppleAuthVerifyProvider,
2325
KeycloakStrategyFactoryProvider,
2426
KeycloakVerifyProvider,
2527
LocalPasswordStrategyFactoryProvider,
@@ -53,6 +55,8 @@ export class AuthenticationComponent implements Component {
5355
.key]: GoogleAuthStrategyFactoryProvider,
5456
[Strategies.Passport.INSTAGRAM_OAUTH2_STRATEGY_FACTORY
5557
.key]: InstagramAuthStrategyFactoryProvider,
58+
[Strategies.Passport.APPLE_OAUTH2_STRATEGY_FACTORY
59+
.key]: AppleAuthStrategyFactoryProvider,
5660
[Strategies.Passport.AZURE_AD_STRATEGY_FACTORY
5761
.key]: AzureADAuthStrategyFactoryProvider,
5862
[Strategies.Passport.KEYCLOAK_STRATEGY_FACTORY
@@ -71,6 +75,7 @@ export class AuthenticationComponent implements Component {
7175
.key]: GoogleAuthVerifyProvider,
7276
[Strategies.Passport.INSTAGRAM_OAUTH2_VERIFIER
7377
.key]: InstagramAuthVerifyProvider,
78+
[Strategies.Passport.APPLE_OAUTH2_VERIFIER.key]: AppleAuthVerifyProvider,
7479
[Strategies.Passport.AZURE_AD_VERIFIER.key]: AzureADAuthVerifyProvider,
7580
[Strategies.Passport.KEYCLOAK_VERIFIER.key]: KeycloakVerifyProvider,
7681
};

src/strategies/keys.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {KeycloakStrategyFactoryProvider} from './passport/passport-keycloak';
88
import {AzureADAuthStrategyFactoryProvider} from './passport/passport-azure-ad';
99
import {VerifyFunction} from './types';
1010
import {InstagramAuthStrategyFactoryProvider} from './passport';
11+
import {AppleAuthStrategyFactoryProvider} from './passport/passport-apple-oauth2';
1112

1213
export namespace Strategies {
1314
export namespace Passport {
@@ -73,5 +74,13 @@ export namespace Strategies {
7374
export const INSTAGRAM_OAUTH2_VERIFIER = BindingKey.create<VerifyFunction.InstagramAuthFn>(
7475
'sf.passport.verifier.instagramOauth2',
7576
);
77+
78+
// Passport-apple-oauth2 strategy
79+
export const APPLE_OAUTH2_STRATEGY_FACTORY = BindingKey.create<AppleAuthStrategyFactoryProvider>(
80+
'sf.passport.strategyFactory.appleOauth2',
81+
);
82+
export const APPLE_OAUTH2_VERIFIER = BindingKey.create<VerifyFunction.AppleAuthFn>(
83+
'sf.passport.verifier.appleOauth2',
84+
);
7685
}
7786
}

src/strategies/passport/index.ts

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

src/strategies/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ 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 AppleStrategy from 'passport-apple';
6+
import {DecodedIdToken} from 'passport-apple';
57
import {IAuthClient, IAuthUser} from '../types';
68

79
export type VerifyCallback = (
@@ -73,6 +75,17 @@ export namespace VerifyFunction {
7375
): Promise<T | null>;
7476
}
7577

78+
export interface AppleAuthFn<T = IAuthUser> extends GenericAuthFn<T> {
79+
(
80+
accessToken: string,
81+
refreshToken: string,
82+
decodedIdToken: DecodedIdToken,
83+
profile: AppleStrategy.Profile,
84+
cb: AppleStrategy.VerifyCallback,
85+
req?: Request,
86+
): Promise<T | null>;
87+
}
88+
7689
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7790
export interface GenericAuthFn<T = any> {
7891
// eslint-disable-next-line @typescript-eslint/no-explicit-any

0 commit comments

Comments
 (0)