@adonisjs/ally 5.0.0-6 → 5.0.0-8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -8
- package/build/chunk-CSAU5B4Q.js +10 -0
- package/build/chunk-CSAU5B4Q.js.map +1 -0
- package/build/chunk-OSVNBZ7V.js +164 -0
- package/build/chunk-OSVNBZ7V.js.map +1 -0
- package/build/chunk-UF7PETXM.js +83 -0
- package/build/chunk-UF7PETXM.js.map +1 -0
- package/build/chunk-UIZWPBWM.js +38 -0
- package/build/chunk-UIZWPBWM.js.map +1 -0
- package/build/chunk-Y3GA4Y2H.js +170 -0
- package/build/chunk-Y3GA4Y2H.js.map +1 -0
- package/build/discord-NGJGLGX7.js +136 -0
- package/build/discord-NGJGLGX7.js.map +1 -0
- package/build/facebook-WTZGLDH5.js +137 -0
- package/build/facebook-WTZGLDH5.js.map +1 -0
- package/build/github-24BPMULZ.js +173 -0
- package/build/github-24BPMULZ.js.map +1 -0
- package/build/google-NMLTQESR.js +184 -0
- package/build/google-NMLTQESR.js.map +1 -0
- package/build/index.d.ts +0 -1
- package/build/index.js +151 -17
- package/build/index.js.map +1 -0
- package/build/linked_in-HIRSEAEP.js +153 -0
- package/build/linked_in-HIRSEAEP.js.map +1 -0
- package/build/providers/ally_provider.js +33 -36
- package/build/providers/ally_provider.js.map +1 -0
- package/build/spotify-ESJDFUD6.js +118 -0
- package/build/spotify-ESJDFUD6.js.map +1 -0
- package/build/twitter-QISDDK24.js +101 -0
- package/build/twitter-QISDDK24.js.map +1 -0
- package/package.json +45 -33
- package/build/configure.js +0 -48
- package/build/src/abstract_drivers/oauth1.js +0 -188
- package/build/src/abstract_drivers/oauth2.js +0 -175
- package/build/src/ally_manager.js +0 -40
- package/build/src/debug.js +0 -10
- package/build/src/define_config.js +0 -75
- package/build/src/drivers/discord.js +0 -156
- package/build/src/drivers/facebook.js +0 -155
- package/build/src/drivers/github.js +0 -213
- package/build/src/drivers/google.js +0 -197
- package/build/src/drivers/linked_in.js +0 -166
- package/build/src/drivers/spotify.js +0 -130
- package/build/src/drivers/twitter.js +0 -117
- package/build/src/errors.js +0 -11
- package/build/src/redirect_request.js +0 -63
- package/build/src/types.js +0 -9
- package/build/stubs/main.js +0 -10
- /package/build/{stubs/config.stub → config/ally.stub} +0 -0
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @adonisjs/ally
|
|
3
|
-
*
|
|
4
|
-
* (c) AdonisJS
|
|
5
|
-
*
|
|
6
|
-
* For the full copyright and license information, please view the LICENSE
|
|
7
|
-
* file that was distributed with this source code.
|
|
8
|
-
*/
|
|
9
|
-
import { Exception } from '@poppinss/utils';
|
|
10
|
-
import { Oauth2Client } from '@poppinss/oauth-client/oauth2';
|
|
11
|
-
import * as errors from '../errors.js';
|
|
12
|
-
import { RedirectRequest } from '../redirect_request.js';
|
|
13
|
-
/**
|
|
14
|
-
* Abstract implementation for an Oauth2 driver
|
|
15
|
-
*/
|
|
16
|
-
export class Oauth2Driver extends Oauth2Client {
|
|
17
|
-
ctx;
|
|
18
|
-
config;
|
|
19
|
-
/**
|
|
20
|
-
* Is the authorization process stateless?
|
|
21
|
-
*/
|
|
22
|
-
isStateless = false;
|
|
23
|
-
/**
|
|
24
|
-
* Oauth client version
|
|
25
|
-
*/
|
|
26
|
-
version = 'oauth2';
|
|
27
|
-
/**
|
|
28
|
-
* The value of state read from the cookies.
|
|
29
|
-
*/
|
|
30
|
-
stateCookieValue;
|
|
31
|
-
constructor(ctx, config) {
|
|
32
|
-
super(config);
|
|
33
|
-
this.ctx = ctx;
|
|
34
|
-
this.config = config;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* The Oauth2Client will use the instance returned from this method to
|
|
38
|
-
* build the redirect url
|
|
39
|
-
*/
|
|
40
|
-
urlBuilder(url) {
|
|
41
|
-
return new RedirectRequest(url, this.scopeParamName, this.scopesSeparator);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Loads the value of state from the cookie and removes it right
|
|
45
|
-
* away. We read the cookie value and clear it during the
|
|
46
|
-
* current request lifecycle.
|
|
47
|
-
*
|
|
48
|
-
* :::::
|
|
49
|
-
* NOTE
|
|
50
|
-
* :::::
|
|
51
|
-
*
|
|
52
|
-
* This child class must call this method inside the constructor.
|
|
53
|
-
*/
|
|
54
|
-
loadState() {
|
|
55
|
-
if (this.isStateless) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
this.stateCookieValue = this.ctx.request.encryptedCookie(this.stateCookieName);
|
|
59
|
-
this.ctx.response.clearCookie(this.stateCookieName);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Persists the state inside the cookie
|
|
63
|
-
*/
|
|
64
|
-
#persistState() {
|
|
65
|
-
if (this.isStateless) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const state = this.getState();
|
|
69
|
-
this.ctx.response.encryptedCookie(this.stateCookieName, state, {
|
|
70
|
-
sameSite: false,
|
|
71
|
-
httpOnly: true,
|
|
72
|
-
});
|
|
73
|
-
return state;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Perform stateless authentication. Only applicable for Oauth2 client
|
|
77
|
-
*/
|
|
78
|
-
stateless() {
|
|
79
|
-
this.isStateless = true;
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Returns the redirect URL for the request.
|
|
84
|
-
*/
|
|
85
|
-
async redirectUrl(callback) {
|
|
86
|
-
const url = this.getRedirectUrl(callback);
|
|
87
|
-
return url;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Redirect user for authorization.
|
|
91
|
-
*/
|
|
92
|
-
async redirect(callback) {
|
|
93
|
-
const url = await this.redirectUrl((request) => {
|
|
94
|
-
const state = this.#persistState();
|
|
95
|
-
state && request.param(this.stateParamName, state);
|
|
96
|
-
if (typeof callback === 'function') {
|
|
97
|
-
callback(request);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
this.ctx.response.redirect(url);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Find if there is a state mismatch
|
|
104
|
-
*/
|
|
105
|
-
stateMisMatch() {
|
|
106
|
-
if (this.isStateless) {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
return this.stateCookieValue !== this.ctx.request.input(this.stateParamName);
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Find if there is an error post redirect
|
|
113
|
-
*/
|
|
114
|
-
hasError() {
|
|
115
|
-
return !!this.getError();
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Get the post redirect error
|
|
119
|
-
*/
|
|
120
|
-
getError() {
|
|
121
|
-
const error = this.ctx.request.input(this.errorParamName);
|
|
122
|
-
if (error) {
|
|
123
|
-
return error;
|
|
124
|
-
}
|
|
125
|
-
if (!this.hasCode()) {
|
|
126
|
-
return 'unknown_error';
|
|
127
|
-
}
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Returns the authorization code
|
|
132
|
-
*/
|
|
133
|
-
getCode() {
|
|
134
|
-
return this.ctx.request.input(this.codeParamName, null);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Find it the code exists
|
|
138
|
-
*/
|
|
139
|
-
hasCode() {
|
|
140
|
-
return !!this.getCode();
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Get access token
|
|
144
|
-
*/
|
|
145
|
-
async accessToken(callback) {
|
|
146
|
-
/**
|
|
147
|
-
* We expect the user to handle errors before calling this method
|
|
148
|
-
*/
|
|
149
|
-
if (this.hasError()) {
|
|
150
|
-
throw new errors.E_OAUTH_MISSING_CODE([this.codeParamName]);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* We expect the user to properly handle the state mis-match use case before
|
|
154
|
-
* calling this method
|
|
155
|
-
*/
|
|
156
|
-
if (this.stateMisMatch()) {
|
|
157
|
-
throw new errors.E_OAUTH_STATE_MISMATCH();
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Get access token by providing the authorization code
|
|
161
|
-
*/
|
|
162
|
-
return this.getAccessToken((request) => {
|
|
163
|
-
request.field(this.codeParamName, this.getCode());
|
|
164
|
-
if (typeof callback === 'function') {
|
|
165
|
-
callback(request);
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Not applicable with Oauth2
|
|
171
|
-
*/
|
|
172
|
-
async userFromTokenAndSecret() {
|
|
173
|
-
throw new Exception('"userFromTokenAndSecret" is not applicable with Oauth2. Use "userFromToken" instead');
|
|
174
|
-
}
|
|
175
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @adonisjs/ally
|
|
3
|
-
*
|
|
4
|
-
* (c) AdonisJS
|
|
5
|
-
*
|
|
6
|
-
* For the full copyright and license information, please view the LICENSE
|
|
7
|
-
* file that was distributed with this source code.
|
|
8
|
-
*/
|
|
9
|
-
import { RuntimeException } from '@poppinss/utils';
|
|
10
|
-
/**
|
|
11
|
-
* AllyManager is used to create instances of a social drivers during an
|
|
12
|
-
* HTTP request. The drivers are cached during the lifecycle of a request.
|
|
13
|
-
*/
|
|
14
|
-
export class AllyManager {
|
|
15
|
-
/**
|
|
16
|
-
* Config with the list of social providers
|
|
17
|
-
*/
|
|
18
|
-
#config;
|
|
19
|
-
#ctx;
|
|
20
|
-
#driversCache = new Map();
|
|
21
|
-
constructor(config, ctx) {
|
|
22
|
-
this.#ctx = ctx;
|
|
23
|
-
this.#config = config;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Returns the driver instance of a social provider
|
|
27
|
-
*/
|
|
28
|
-
use(provider) {
|
|
29
|
-
if (this.#driversCache.has(provider)) {
|
|
30
|
-
return this.#driversCache.get(provider);
|
|
31
|
-
}
|
|
32
|
-
const driver = this.#config[provider];
|
|
33
|
-
if (!driver) {
|
|
34
|
-
throw new RuntimeException(`Unknown ally provider "${String(provider)}". Make sure it is registered inside the config/ally.ts file`);
|
|
35
|
-
}
|
|
36
|
-
const driverInstance = driver(this.#ctx);
|
|
37
|
-
this.#driversCache.set(provider, driverInstance);
|
|
38
|
-
return driverInstance;
|
|
39
|
-
}
|
|
40
|
-
}
|
package/build/src/debug.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @adonisjs/ally
|
|
3
|
-
*
|
|
4
|
-
* (c) AdonisJS
|
|
5
|
-
*
|
|
6
|
-
* For the full copyright and license information, please view the LICENSE
|
|
7
|
-
* file that was distributed with this source code.
|
|
8
|
-
*/
|
|
9
|
-
import { debuglog } from 'node:util';
|
|
10
|
-
export default debuglog('adonisjs:ally');
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @adonisjs/ally
|
|
3
|
-
*
|
|
4
|
-
* (c) Ally
|
|
5
|
-
*
|
|
6
|
-
* For the full copyright and license information, please view the LICENSE
|
|
7
|
-
* file that was distributed with this source code.
|
|
8
|
-
*/
|
|
9
|
-
import { configProvider } from '@adonisjs/core';
|
|
10
|
-
/**
|
|
11
|
-
* Define config for the ally
|
|
12
|
-
*/
|
|
13
|
-
export function defineConfig(config) {
|
|
14
|
-
return configProvider.create(async (app) => {
|
|
15
|
-
const serviceNames = Object.keys(config);
|
|
16
|
-
const services = {};
|
|
17
|
-
for (let serviceName of serviceNames) {
|
|
18
|
-
const service = config[serviceName];
|
|
19
|
-
if (typeof service === 'function') {
|
|
20
|
-
services[serviceName] = service;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
services[serviceName] = await service.resolver(app);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return services;
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Helpers to configure social auth services
|
|
31
|
-
*/
|
|
32
|
-
export const services = {
|
|
33
|
-
discord(config) {
|
|
34
|
-
return configProvider.create(async () => {
|
|
35
|
-
const { DiscordDriver } = await import('./drivers/discord.js');
|
|
36
|
-
return (ctx) => new DiscordDriver(ctx, config);
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
facebook(config) {
|
|
40
|
-
return configProvider.create(async () => {
|
|
41
|
-
const { FacebookDriver } = await import('./drivers/facebook.js');
|
|
42
|
-
return (ctx) => new FacebookDriver(ctx, config);
|
|
43
|
-
});
|
|
44
|
-
},
|
|
45
|
-
github(config) {
|
|
46
|
-
return configProvider.create(async () => {
|
|
47
|
-
const { GithubDriver } = await import('./drivers/github.js');
|
|
48
|
-
return (ctx) => new GithubDriver(ctx, config);
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
google(config) {
|
|
52
|
-
return configProvider.create(async () => {
|
|
53
|
-
const { GoogleDriver } = await import('./drivers/google.js');
|
|
54
|
-
return (ctx) => new GoogleDriver(ctx, config);
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
linkedin(config) {
|
|
58
|
-
return configProvider.create(async () => {
|
|
59
|
-
const { LinkedInDriver } = await import('./drivers/linked_in.js');
|
|
60
|
-
return (ctx) => new LinkedInDriver(ctx, config);
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
spotify(config) {
|
|
64
|
-
return configProvider.create(async () => {
|
|
65
|
-
const { SpotifyDriver } = await import('./drivers/spotify.js');
|
|
66
|
-
return (ctx) => new SpotifyDriver(ctx, config);
|
|
67
|
-
});
|
|
68
|
-
},
|
|
69
|
-
twitter(config) {
|
|
70
|
-
return configProvider.create(async () => {
|
|
71
|
-
const { TwitterDriver } = await import('./drivers/twitter.js');
|
|
72
|
-
return (ctx) => new TwitterDriver(ctx, config);
|
|
73
|
-
});
|
|
74
|
-
},
|
|
75
|
-
};
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @adonisjs/ally
|
|
3
|
-
*
|
|
4
|
-
* (c) AdonisJS
|
|
5
|
-
*
|
|
6
|
-
* For the full copyright and license information, please view the LICENSE
|
|
7
|
-
* file that was distributed with this source code.
|
|
8
|
-
*/
|
|
9
|
-
import { Oauth2Driver } from '../abstract_drivers/oauth2.js';
|
|
10
|
-
/**
|
|
11
|
-
* Discord driver to login user via Discord
|
|
12
|
-
*/
|
|
13
|
-
export class DiscordDriver extends Oauth2Driver {
|
|
14
|
-
config;
|
|
15
|
-
accessTokenUrl = 'https://discord.com/api/oauth2/token';
|
|
16
|
-
authorizeUrl = 'https://discord.com/api/oauth2/authorize';
|
|
17
|
-
userInfoUrl = 'https://discord.com/api/users/@me';
|
|
18
|
-
/**
|
|
19
|
-
* The param name for the authorization code
|
|
20
|
-
*/
|
|
21
|
-
codeParamName = 'code';
|
|
22
|
-
/**
|
|
23
|
-
* The param name for the error
|
|
24
|
-
*/
|
|
25
|
-
errorParamName = 'error';
|
|
26
|
-
/**
|
|
27
|
-
* Cookie name for storing the "discord_oauth_state"
|
|
28
|
-
*/
|
|
29
|
-
stateCookieName = 'discord_oauth_state';
|
|
30
|
-
/**
|
|
31
|
-
* Parameter name to be used for sending and receiving the state
|
|
32
|
-
* from Discord
|
|
33
|
-
*/
|
|
34
|
-
stateParamName = 'state';
|
|
35
|
-
/**
|
|
36
|
-
* Parameter name for defining the scopes
|
|
37
|
-
*/
|
|
38
|
-
scopeParamName = 'scope';
|
|
39
|
-
/**
|
|
40
|
-
* Scopes separator
|
|
41
|
-
*/
|
|
42
|
-
scopesSeparator = ' ';
|
|
43
|
-
constructor(ctx, config) {
|
|
44
|
-
super(ctx, config);
|
|
45
|
-
this.config = config;
|
|
46
|
-
/**
|
|
47
|
-
* Extremely important to call the following method to clear the
|
|
48
|
-
* state set by the redirect request
|
|
49
|
-
*/
|
|
50
|
-
this.loadState();
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Configuring the redirect request with defaults
|
|
54
|
-
*/
|
|
55
|
-
configureRedirectRequest(request) {
|
|
56
|
-
/**
|
|
57
|
-
* Define user defined scopes or the default one's
|
|
58
|
-
*/
|
|
59
|
-
request.scopes(this.config.scopes || ['identify', 'email']);
|
|
60
|
-
request.param('response_type', 'code');
|
|
61
|
-
request.param('grant_type', 'authorization_code');
|
|
62
|
-
/**
|
|
63
|
-
* Define params based upon user config
|
|
64
|
-
*/
|
|
65
|
-
if (this.config.prompt) {
|
|
66
|
-
request.param('prompt', this.config.prompt);
|
|
67
|
-
}
|
|
68
|
-
if (this.config.guildId) {
|
|
69
|
-
request.param('guild_id', this.config.guildId);
|
|
70
|
-
}
|
|
71
|
-
if (this.config.disableGuildSelect !== undefined) {
|
|
72
|
-
request.param('disable_guild_select', this.config.disableGuildSelect);
|
|
73
|
-
}
|
|
74
|
-
if (this.config.permissions !== undefined) {
|
|
75
|
-
request.param('permissions', this.config.permissions);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Configuring the access token API request to send extra fields
|
|
80
|
-
*/
|
|
81
|
-
configureAccessTokenRequest(request) {
|
|
82
|
-
/**
|
|
83
|
-
* Send state to Discord when request is not stateles
|
|
84
|
-
*/
|
|
85
|
-
if (!this.isStateless) {
|
|
86
|
-
request.field('state', this.stateCookieValue);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Returns the HTTP request with the authorization header set
|
|
91
|
-
*/
|
|
92
|
-
getAuthenticatedRequest(url, token) {
|
|
93
|
-
const request = this.httpClient(url);
|
|
94
|
-
request.header('Authorization', `Bearer ${token}`);
|
|
95
|
-
request.header('Accept', 'application/json');
|
|
96
|
-
request.parseAs('json');
|
|
97
|
-
return request;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Fetches the user info from the Discord API
|
|
101
|
-
* https://discord.com/developers/docs/resources/user#get-current-user
|
|
102
|
-
*/
|
|
103
|
-
async getUserInfo(token, callback) {
|
|
104
|
-
const request = this.getAuthenticatedRequest(this.config.userInfoUrl || this.userInfoUrl, token);
|
|
105
|
-
if (typeof callback === 'function') {
|
|
106
|
-
callback(request);
|
|
107
|
-
}
|
|
108
|
-
const body = await request.get();
|
|
109
|
-
return {
|
|
110
|
-
id: body.id,
|
|
111
|
-
name: `${body.username}#${body.discriminator}`,
|
|
112
|
-
nickName: body.username,
|
|
113
|
-
avatarUrl: body.avatar
|
|
114
|
-
? `https://cdn.discordapp.com/avatars/${body.id}/${body.avatar}.${body.avatar.startsWith('a_') ? 'gif' : 'png'}`
|
|
115
|
-
: `https://cdn.discordapp.com/embed/avatars/${body.discriminator % 5}.png`,
|
|
116
|
-
email: body.email,
|
|
117
|
-
emailVerificationState: 'verified' in body
|
|
118
|
-
? body.verified
|
|
119
|
-
? 'verified'
|
|
120
|
-
: 'unverified'
|
|
121
|
-
: 'unsupported',
|
|
122
|
-
original: body,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Find if the current error code is for access denied
|
|
127
|
-
*/
|
|
128
|
-
accessDenied() {
|
|
129
|
-
const error = this.getError();
|
|
130
|
-
if (!error) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
return error === 'access_denied';
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Returns details for the authorized user
|
|
137
|
-
*/
|
|
138
|
-
async user(callback) {
|
|
139
|
-
const token = await this.accessToken(callback);
|
|
140
|
-
const user = await this.getUserInfo(token.token, callback);
|
|
141
|
-
return {
|
|
142
|
-
...user,
|
|
143
|
-
token,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Finds the user by the access token
|
|
148
|
-
*/
|
|
149
|
-
async userFromToken(token, callback) {
|
|
150
|
-
const user = await this.getUserInfo(token, callback);
|
|
151
|
-
return {
|
|
152
|
-
...user,
|
|
153
|
-
token: { token, type: 'bearer' },
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @adonisjs/ally
|
|
3
|
-
*
|
|
4
|
-
* (c) AdonisJS
|
|
5
|
-
*
|
|
6
|
-
* For the full copyright and license information, please view the LICENSE
|
|
7
|
-
* file that was distributed with this source code.
|
|
8
|
-
*/
|
|
9
|
-
import { Oauth2Driver } from '../abstract_drivers/oauth2.js';
|
|
10
|
-
/**
|
|
11
|
-
* Facebook driver to login user via Facebook
|
|
12
|
-
*/
|
|
13
|
-
export class FacebookDriver extends Oauth2Driver {
|
|
14
|
-
config;
|
|
15
|
-
accessTokenUrl = 'https://graph.facebook.com/v10.0/oauth/access_token';
|
|
16
|
-
authorizeUrl = 'https://www.facebook.com/v10.0/dialog/oauth';
|
|
17
|
-
userInfoUrl = 'https://graph.facebook.com/v10.0/me';
|
|
18
|
-
/**
|
|
19
|
-
* The default set of fields to query for the user request
|
|
20
|
-
*/
|
|
21
|
-
userFields = [
|
|
22
|
-
'name',
|
|
23
|
-
'first_name',
|
|
24
|
-
'last_name',
|
|
25
|
-
'link',
|
|
26
|
-
'email',
|
|
27
|
-
'picture.width(400).height(400)',
|
|
28
|
-
'verified',
|
|
29
|
-
];
|
|
30
|
-
/**
|
|
31
|
-
* The param name for the authorization code
|
|
32
|
-
*/
|
|
33
|
-
codeParamName = 'code';
|
|
34
|
-
/**
|
|
35
|
-
* The param name for the error
|
|
36
|
-
*/
|
|
37
|
-
errorParamName = 'error';
|
|
38
|
-
/**
|
|
39
|
-
* Cookie name for storing the "facebok_oauth_state"
|
|
40
|
-
*/
|
|
41
|
-
stateCookieName = 'facebok_oauth_state';
|
|
42
|
-
/**
|
|
43
|
-
* Parameter name to be used for sending and receiving the state
|
|
44
|
-
* from Facebok
|
|
45
|
-
*/
|
|
46
|
-
stateParamName = 'state';
|
|
47
|
-
/**
|
|
48
|
-
* Parameter name for defining the scopes
|
|
49
|
-
*/
|
|
50
|
-
scopeParamName = 'scope';
|
|
51
|
-
/**
|
|
52
|
-
* Scopes separator
|
|
53
|
-
*/
|
|
54
|
-
scopesSeparator = ' ';
|
|
55
|
-
constructor(ctx, config) {
|
|
56
|
-
super(ctx, config);
|
|
57
|
-
this.config = config;
|
|
58
|
-
/**
|
|
59
|
-
* Extremely important to call the following method to clear the
|
|
60
|
-
* state set by the redirect request
|
|
61
|
-
*/
|
|
62
|
-
this.loadState();
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Configuring the redirect request with defaults
|
|
66
|
-
*/
|
|
67
|
-
configureRedirectRequest(request) {
|
|
68
|
-
/**
|
|
69
|
-
* Define user defined scopes or the default one's
|
|
70
|
-
*/
|
|
71
|
-
request.scopes(this.config.scopes || ['email']);
|
|
72
|
-
request.param('response_type', 'code');
|
|
73
|
-
request.param('grant_type', 'authorization_code');
|
|
74
|
-
/**
|
|
75
|
-
* Define params based upon user config
|
|
76
|
-
*/
|
|
77
|
-
if (this.config.display) {
|
|
78
|
-
request.param('display', this.config.display);
|
|
79
|
-
}
|
|
80
|
-
if (this.config.authType) {
|
|
81
|
-
request.param('auth_type', this.config.authType);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Returns the HTTP request with the authorization header set
|
|
86
|
-
*/
|
|
87
|
-
getAuthenticatedRequest(url, token) {
|
|
88
|
-
const request = this.httpClient(url);
|
|
89
|
-
request.header('Authorization', `Bearer ${token}`);
|
|
90
|
-
request.header('Accept', 'application/json');
|
|
91
|
-
request.parseAs('json');
|
|
92
|
-
return request;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Fetches the user info from the Facebook API
|
|
96
|
-
* https://developers.facebook.com/docs/graph-api/reference/user/
|
|
97
|
-
*/
|
|
98
|
-
async getUserInfo(token, callback) {
|
|
99
|
-
const request = this.getAuthenticatedRequest(this.config.userInfoUrl || this.userInfoUrl, token);
|
|
100
|
-
request.param('fields', (this.config.userFields || this.userFields).join(','));
|
|
101
|
-
const body = await request.get();
|
|
102
|
-
/**
|
|
103
|
-
* Invoke callback if defined
|
|
104
|
-
*/
|
|
105
|
-
if (typeof callback === 'function') {
|
|
106
|
-
callback(request);
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
id: body.id,
|
|
110
|
-
name: body.name,
|
|
111
|
-
nickName: body.name,
|
|
112
|
-
// https://developers.facebook.com/docs/graph-api/reference/user/picture/
|
|
113
|
-
avatarUrl: body.picture?.data?.url || null,
|
|
114
|
-
email: body.email || null,
|
|
115
|
-
// Important note: https://developers.facebook.com/docs/facebook-login/multiple-providers#postfb1
|
|
116
|
-
emailVerificationState: 'verified' in body
|
|
117
|
-
? body.verified
|
|
118
|
-
? 'verified'
|
|
119
|
-
: 'unverified'
|
|
120
|
-
: 'unsupported',
|
|
121
|
-
original: body,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Find if the current error code is for access denied
|
|
126
|
-
*/
|
|
127
|
-
accessDenied() {
|
|
128
|
-
const error = this.getError();
|
|
129
|
-
if (!error) {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
return error === 'access_denied';
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Returns details for the authorized user
|
|
136
|
-
*/
|
|
137
|
-
async user(callback) {
|
|
138
|
-
const token = await this.accessToken(callback);
|
|
139
|
-
const user = await this.getUserInfo(token.token, callback);
|
|
140
|
-
return {
|
|
141
|
-
...user,
|
|
142
|
-
token,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Finds the user by the access token
|
|
147
|
-
*/
|
|
148
|
-
async userFromToken(token, callback) {
|
|
149
|
-
const user = await this.getUserInfo(token, callback);
|
|
150
|
-
return {
|
|
151
|
-
...user,
|
|
152
|
-
token: { token, type: 'bearer' },
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|