mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Add authSubject and authProvider to sessions
Summary: This also updates Authorizer to link the authSubject to Grist users if not previously linked. Linked subjects are now used as the username for password-based logins, instead of emails, which remain as a fallback. Test Plan: Existing tests, and tested login flows manually. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D3356
This commit is contained in:
@@ -3,6 +3,7 @@ import {OpenDocMode} from 'app/common/DocListAPI';
|
||||
import {ErrorWithCode} from 'app/common/ErrorWithCode';
|
||||
import {FullUser, UserProfile} from 'app/common/LoginSessionAPI';
|
||||
import {canEdit, canView, getWeakestRole, Role} from 'app/common/roles';
|
||||
import {UserOptions} from 'app/common/UserAPI';
|
||||
import {Document} from 'app/gen-server/entity/Document';
|
||||
import {User} from 'app/gen-server/entity/User';
|
||||
import {DocAuthKey, DocAuthResult, HomeDBManager} from 'app/gen-server/lib/HomeDBManager';
|
||||
@@ -243,8 +244,14 @@ export async function addRequestUser(dbManager: HomeDBManager, permitStore: IPer
|
||||
// Modify request session object to link the current org with our choice of
|
||||
// profile. Express-session will save this change.
|
||||
sessionUser = linkOrgWithEmail(session, option.email, mreq.org);
|
||||
const userOptions: UserOptions = {};
|
||||
if (sessionUser?.profile?.loginMethod === 'Email + Password') {
|
||||
// Link the session authSubject, if present, to the user. This has no effect
|
||||
// if the user already has an authSubject set in the db.
|
||||
userOptions.authSubject = sessionUser.authSubject;
|
||||
}
|
||||
// In this special case of initially linking a profile, we need to look up the user's info.
|
||||
mreq.user = await dbManager.getUserByLogin(option.email);
|
||||
mreq.user = await dbManager.getUserByLogin(option.email, {userOptions});
|
||||
mreq.userId = option.id;
|
||||
mreq.userIsAuthorized = true;
|
||||
} else {
|
||||
@@ -263,12 +270,18 @@ export async function addRequestUser(dbManager: HomeDBManager, permitStore: IPer
|
||||
}
|
||||
}
|
||||
|
||||
profile = sessionUser && sessionUser.profile || undefined;
|
||||
profile = sessionUser?.profile ?? undefined;
|
||||
|
||||
// If we haven't computed a userId yet, check for one using an email address in the profile.
|
||||
// A user record will be created automatically for emails we've never seen before.
|
||||
if (profile && !mreq.userId) {
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, profile);
|
||||
const userOptions: UserOptions = {};
|
||||
if (profile?.loginMethod === 'Email + Password') {
|
||||
// Link the session authSubject, if present, to the user. This has no effect
|
||||
// if the user already has an authSubject set in the db.
|
||||
userOptions.authSubject = sessionUser.authSubject;
|
||||
}
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, {profile, userOptions});
|
||||
if (user) {
|
||||
mreq.user = user;
|
||||
mreq.userId = user.id;
|
||||
@@ -280,7 +293,7 @@ export async function addRequestUser(dbManager: HomeDBManager, permitStore: IPer
|
||||
if (!mreq.userId) {
|
||||
profile = getRequestProfile(mreq);
|
||||
if (profile) {
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, profile);
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, {profile});
|
||||
if(user) {
|
||||
mreq.user = user;
|
||||
mreq.users = [profile];
|
||||
@@ -290,7 +303,6 @@ export async function addRequestUser(dbManager: HomeDBManager, permitStore: IPer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If no userId has been found yet, fall back on anonymous.
|
||||
if (!mreq.userId) {
|
||||
const anon = dbManager.getAnonymousUser();
|
||||
|
||||
@@ -19,9 +19,17 @@ export interface SessionUserObj {
|
||||
*/
|
||||
lastLoginTimestamp?: number;
|
||||
|
||||
// [UNUSED] Authentication provider string indicating the login method used.
|
||||
/**
|
||||
* The authentication provider. (Typically the JWT "iss".)
|
||||
*/
|
||||
authProvider?: string;
|
||||
|
||||
/**
|
||||
* Identifier for the user from the authentication provider. (Typically
|
||||
* the JWT "sub".)
|
||||
*/
|
||||
authSubject?: string;
|
||||
|
||||
// [UNUSED] Login ID token used to access AWS services.
|
||||
idToken?: string;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function getMinimalLoginSystem(): Promise<GristLoginSystem> {
|
||||
// If working without a login system, make sure default user exists.
|
||||
const dbManager = gristServer.getHomeDBManager();
|
||||
const profile = getDefaultProfile();
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, profile);
|
||||
const user = await dbManager.getUserByLoginWithRetry(profile.email, {profile});
|
||||
if (user) {
|
||||
// No need to survey this user!
|
||||
user.isFirstTimeUser = false;
|
||||
|
||||
@@ -21,6 +21,7 @@ export const TEST_HTTPS_OFFSET = process.env.GRIST_TEST_HTTPS_OFFSET ?
|
||||
const INTERNAL_FIELDS = new Set([
|
||||
'apiKey', 'billingAccountId', 'firstLoginAt', 'filteredOut', 'ownerId', 'gracePeriodStart', 'stripeCustomerId',
|
||||
'stripeSubscriptionId', 'stripePlanId', 'stripeProductId', 'userId', 'isFirstTimeUser', 'allowGoogleLogin',
|
||||
'authSubject',
|
||||
]);
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user