Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8d0e78b

Browse files
fix(Google Login): Prevents uninvited users gaining access (#1517 - LLC-10)
#1517https://learningpool.atlassian.net/browse/LLC-10
1 parent334935f commit8d0e78b

File tree

13 files changed

+208
-30
lines changed

13 files changed

+208
-30
lines changed

‎api/src/auth/jwt.js‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const payloadDefaults = ({
5353
/**
5454
*@param {*} - payload
5555
*@param {*} opts - (optional)
56-
*@returns {Promise<any>}
56+
*@returns {Promise<string>}
5757
*/
5858
constcreateJWT=({
5959
userId,
@@ -84,7 +84,7 @@ const createUserTokenPayload = (user, provider) => payloadDefaults({
8484
/**
8585
*@param {*} user
8686
*@param {*} provider
87-
*@returns {Promise<any>}
87+
*@returns {Promise<string>}
8888
*/
8989
constcreateUserJWT=(user,provider)=>
9090
createJWT(
@@ -103,7 +103,7 @@ const createUserRefreshTokenPayload = (user, provider) => payloadDefaults({
103103
/**
104104
*@param {*} user
105105
*@param {*} provider
106-
*@returns {Promise<any>}
106+
*@returns {Promise<string>}
107107
*/
108108
constcreateUserRefreshJWT=(user,provider)=>
109109
createJWT(
@@ -150,7 +150,7 @@ const createOrgRefreshTokenPayload = (user, organisationId, provider) => payload
150150
*@param {*} user
151151
*@param {*} organisationId
152152
*@param {*} provider
153-
*@returns {Promise<any>}
153+
*@returns {Promise<string>}
154154
*/
155155
constcreateOrgRefreshJWT=(user,organisationId,provider)=>
156156
createJWT(

‎api/src/auth/passport.js‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,25 @@ if (
212212
profile.emails,
213213
email=>email.verified===true
214214
);
215-
User.findOrCreate({email:userEmail.value},(err,user)=>{
216-
assert.ifError(err);
217-
user.googleId=profile.id;
218-
user.imageUrl=get(profile,'photos.0.value');
219-
user.name=profile.displayName;
220-
user.save((err,savedUser)=>done(err,savedUser));
221-
});
215+
216+
User.findOne(
217+
{
218+
email:userEmail.value
219+
},
220+
(err,user)=>{
221+
assert.ifError(err);
222+
223+
if(!user){
224+
returndone(null,false,{message:'User does not exist'});
225+
}
226+
227+
user.googleId=profile.id;
228+
user.imageUrl=get(profile,'photos.0.value');
229+
user.name=profile.displayName;
230+
231+
user.save((err,savedUser)=>done(err,savedUser));
232+
},
233+
);
222234
}
223235
)
224236
);

‎api/src/controllers/AuthController.js‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import passport from 'passport';
22
importjsonwebtokenfrom'jsonwebtoken';
33
import{v4asuuid}from'uuid';
44
importmsfrom'ms';
5+
importstatusfrom'http-status';
56
importloggerfrom'lib/logger';
67
importUserfrom'lib/models/user';
78
importOAuthTokenfrom'lib/models/oAuthToken';
@@ -261,12 +262,18 @@ const jwtOrganisation = (req, res) => {
261262
}
262263
};
263264

264-
constgoogleSuccess=(req,res)=>{
265+
constgoogleSuccess=(user,response)=>{
265266
// we have successfully signed into google
266267
// create a JWT and set it in the query params (only way to return it with a redirect)
267-
createUserJWT(req.user,'google')
268-
.then(token=>res.redirect(`/api${AUTH_JWT_SUCCESS}?access_token=${token}`))
269-
.catch(err=>res.status(500).send(err));
268+
createUserJWT(user,'google')
269+
.then(token=>response.redirect(`/api${AUTH_JWT_SUCCESS}?access_token=${token}`))
270+
.catch(
271+
(err)=>{
272+
response
273+
.status(status.INTERNAL_SERVER_ERROR)
274+
.send(err);
275+
}
276+
);
270277
};
271278

272279
constclientInfo=(req,res)=>{

‎api/src/routes/HttpRoutes.js‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ router.get(
117117
AuthController.clientInfo
118118
);
119119

120+
router.get(
121+
routes.OAUTH2_FAILED,
122+
(request,response)=>{
123+
response.send('Authorization failed');
124+
},
125+
);
126+
120127
router.post(
121128
routes.OAUTH2_TOKEN,
122129
AuthController.issueOAuth2AccessToken
@@ -136,10 +143,26 @@ if (process.env.GOOGLE_ENABLED) {
136143
routes.AUTH_JWT_GOOGLE,
137144
passport.authenticate('google',GOOGLE_AUTH_OPTIONS)
138145
);
146+
139147
router.get(
140148
routes.AUTH_JWT_GOOGLE_CALLBACK,
141-
passport.authenticate('google',DEFAULT_PASSPORT_OPTIONS),
142-
AuthController.googleSuccess
149+
(request,response,next)=>{
150+
passport.authenticate(
151+
'google',
152+
DEFAULT_PASSPORT_OPTIONS,
153+
(error,user,info)=>{
154+
constdefaultErrorMessage='Something bad happened';
155+
156+
if(!user){
157+
response.redirect(`/api${routes.OAUTH2_FAILED}?error=${get(info,'message',defaultErrorMessage)}`);
158+
159+
return;
160+
}
161+
162+
AuthController.googleSuccess(user,response);
163+
},
164+
)(request,response,next);
165+
},
143166
);
144167
}
145168

‎lib/constants/auth.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import getAuthFromRequest from 'lib/helpers/getAuthFromRequest';
1919

2020
exportconstGOOGLE_AUTH_OPTIONS={
2121
scope:['https://www.googleapis.com/auth/plus.login','email'],
22-
approvalPrompt:'auto',
23-
session:false
22+
session:false,
23+
prompt:'select_account'
2424
};
2525

2626
exportconstDEFAULT_PASSPORT_OPTIONS={

‎lib/constants/routes.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const AUTH_JWT_SUCCESS = '/auth/jwt/success';
1515
exportconstAUTH_CLIENT_INFO='/auth/client/info';
1616

1717
exportconstOAUTH2_TOKEN='/oauth2/token';
18+
exportconstOAUTH2_FAILED='/oauth2/failed';
1819

1920
exportconstSENDSMS='/sendsms';
2021

‎lib/models/user.js‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,73 @@ import getOrgFromAuthInfo from 'lib/services/auth/authInfoSelectors/getOrgFromAu
1717
importgetUserIdFromAuthInfofrom'lib/services/auth/authInfoSelectors/getUserIdFromAuthInfo';
1818
import{validatePasswordUtil}from'lib/utils/validators/User';
1919

20+
/**
21+
*@typedef {object} PasswordHistoryItem
22+
*@property {string} hash
23+
*@property {Date} date
24+
*/
25+
26+
/**
27+
*@typedef {object} ResetTokensItem
28+
*@property {string} token
29+
*@property {Date} expires
30+
*/
31+
32+
/**
33+
*@typedef {object} UserSettings
34+
*@property {boolean} CONFIRM_BEFORE_DELETE
35+
*/
36+
37+
/**
38+
*@typedef {object} OrganisationSettings
39+
*@property {object} organisation - TODO: define type
40+
*@property {string[]} scopes
41+
*@property {object[]} roles - TODO: define type
42+
*@property {string} filter
43+
*@property {string} oldFilter
44+
*@property {string} timezone
45+
*/
46+
47+
/**
48+
*@typedef {object} OwnerOrganisationSettings
49+
*@property {boolean} LOCKOUT_ENABLED
50+
*@property {number} LOCKOUT_ATTEMPS
51+
*@property {number} LOCKOUT_SECONDS
52+
*@property {boolean} PASSWORD_HISTORY_CHECK
53+
*@property {number} PASSWORD_HISTORY_TOTAL
54+
*@property {number} PASSWORD_MIN_LENGTH
55+
*@property {boolean} PASSWORD_REQUIRE_ALPHA
56+
*@property {boolean} PASSWORD_REQUIRE_NUMBER
57+
*@property {boolean} PASSWORD_USE_CUSTOM_REGEX
58+
*@property {string} PASSWORD_CUSTOM_REGEX
59+
*@property {string} PASSWORD_CUSTOM_MESSAGE
60+
*/
61+
62+
/**
63+
* Plain object structure without mongoose model methods
64+
*@typedef {object} User
65+
*@property {string} name
66+
*@property {string} email
67+
*@property {object[]} organisations - TODO: define type
68+
*@property {OrganisationSettings[]} organisationSettings
69+
*@property {string} imageUrl
70+
*@property {string} googleId
71+
*@property {string} password
72+
*@property {object} ownerOrganisation - TODO: define type
73+
*@property {OwnerOrganisationSettings} ownerOrganisationSettings
74+
*@property {UserSettings} settings
75+
*@property {string[]} scopes
76+
*@property {boolean} verified
77+
*@property {ResetTokensItem[]} resetTokens
78+
*@property {PasswordHistoryItem[]} passwordHistory
79+
*@property {Date} authLastAttempt
80+
*@property {number} authFailedAttempts
81+
*@property {Date} authLockoutExpiry
82+
*@property {boolean} hasBeenMigrated
83+
*/
84+
85+
/**@typedef {module:mongoose.Model<User>} UserModel */
86+
2087
/**
2188
*@param {string} value
2289
*@returns {true} if validation is success
@@ -63,6 +130,7 @@ async function validatePassword(value) {
63130
}
64131

65132
// TODO: Remove `organisationSettings.oldFilter` and `hasBeenMigrated` after we confirm success of $in migration
133+
/**@class UserSchema */
66134
constschema=newmongoose.Schema({
67135
name:{type:String},
68136
email:{
@@ -110,6 +178,7 @@ const schema = new mongoose.Schema({
110178
// "owned" users when the organisation's settings are updated
111179
ownerOrganisationSettings:{
112180
LOCKOUT_ENABLED:{type:Boolean,default:true},
181+
// TODO: fix typo 🤨
113182
LOCKOUT_ATTEMPS:{type:Number,default:5},// number of attempts before lock out
114183
LOCKOUT_SECONDS:{type:Number,default:1800},// 30 minute lock out period
115184

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"highland":"^2.8.1",
8888
"html-to-text":"^2.1.0",
8989
"http-proxy":"^1.12.0",
90+
"http-status":"^1.4.2",
9091
"immutable":"^3.8.1",
9192
"ioredis":"^3.2.2",
9293
"js-cookie":"^2.1.3",

‎ui/src/containers/App/index.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { routeNodeSelector } from 'redux-router5';
1010
import{startsWithSegment}from'router5.helpers';
1111
importgetfrom'lodash/get';
1212
importcreateAsyncComponentfrom'ui/utils/createAsyncComponent';
13+
importSaveBarErrorsfrom'ui/containers/SaveBarErrors';
1314

1415
constrenderPage=(routeName)=>{
1516
consttestRoute=startsWithSegment(routeName);
@@ -62,6 +63,7 @@ const component = ({ route }) => {
6263
<Helmet{...config.app.head}/>
6364
{renderPage(name)}
6465
<Toasts/>
66+
<SaveBarErrors/>
6567
</div>
6668
);
6769
};

‎ui/src/pages/LoginPage/index.js‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ const enhance = compose(
3535
})
3636
);
3737

38+
/**
39+
*@param oAuthLoginStart - {@see reduxOAuthLoginStart}
40+
*@param loginStart - {@see reduxLoginStart}
41+
*@param loginRequestState - {@see loginRequestStateSelector}
42+
*@param loginError - {@see loginErrorSelector}
43+
*@param googleAuth - {@see getAppDataSelector}
44+
*@returns {*}
45+
*/
3846
constrender=({
3947
oAuthLoginStart,
4048
loginStart,
@@ -52,8 +60,11 @@ const render = ({
5260
if(email&&password)loginStart({username:email, password}).catch(()=>{});
5361
};
5462

55-
constonClickOAuthLogin=(e)=>{
56-
if(e)e.preventDefault();
63+
constonClickOAuthLogin=(event)=>{
64+
if(event){
65+
event.preventDefault();
66+
}
67+
5768
oAuthLoginStart('google').catch(()=>{});
5869
};
5970

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp