mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Add new Grist login page
Summary: Adds a new Grist login page to the login app, and replaces the server-side Cognito Google Sign-In flow with Google's own OAuth flow. Test Plan: Browser and server tests. Reviewers: jarek Reviewed By: jarek Differential Revision: https://phab.getgrist.com/D3332
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
import {parseSubdomain} from 'app/common/gristUrls';
|
||||
|
||||
// This interface is used by the standalone login-connect tool for knowing where to redirect to,
|
||||
// by Client.ts to construct this info, and by CognitoClient to decide what to do.
|
||||
|
||||
export interface LoginState {
|
||||
// Locally-running Grist uses localPort, while hosted uses subdomain. Login-connect uses this to
|
||||
// redirect back to the localhost or to the subdomain.
|
||||
localPort?: number;
|
||||
subdomain?: string;
|
||||
baseDomain?: string; // the domain with the (left-most) subdomain removed, e.g. ".getgrist.com".
|
||||
// undefined on localhost.
|
||||
|
||||
// Standalone version sets clientId, used later to find the LoginSession. Hosted and dev
|
||||
// versions rely on the browser cookies instead, specifically on the session cookie.
|
||||
clientId?: string;
|
||||
|
||||
// Hosted and dev versions set redirectUrl and redirect to it when login or logout completes.
|
||||
// Standalone version omits redirectUrl, and serves a page which closes the window.
|
||||
redirectUrl?: string;
|
||||
}
|
||||
|
||||
/// Allowed localhost addresses.
|
||||
export const localhostRegex = /^localhost(?::(\d+))?$/i;
|
||||
|
||||
export function getLoginState(reqHost: string): LoginState|null {
|
||||
const {org, base} = parseSubdomain(reqHost);
|
||||
const matchPort = localhostRegex.exec(reqHost);
|
||||
return org ? {subdomain: org, baseDomain: base} :
|
||||
matchPort ? {localPort: matchPort[1] ? parseInt(matchPort[1], 10) : 80} : null;
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import {BillingPage, BillingSubPage, BillingTask} from 'app/common/BillingAPI';
|
||||
import {OpenDocMode} from 'app/common/DocListAPI';
|
||||
import {EngineCode} from 'app/common/DocumentSettings';
|
||||
import {encodeQueryParams, isAffirmative} from 'app/common/gutil';
|
||||
import {localhostRegex} from 'app/common/LoginState';
|
||||
import {LocalPlugin} from 'app/common/plugin';
|
||||
import {StringUnion} from 'app/common/StringUnion';
|
||||
import {UIRowId} from 'app/common/UIRowId';
|
||||
@@ -34,7 +33,7 @@ export type WelcomePage = typeof WelcomePage.type;
|
||||
export const AccountPage = StringUnion('account');
|
||||
export type AccountPage = typeof AccountPage.type;
|
||||
|
||||
export const LoginPage = StringUnion('signup', 'verified', 'forgot-password');
|
||||
export const LoginPage = StringUnion('signup', 'login', 'verified', 'forgot-password');
|
||||
export type LoginPage = typeof LoginPage.type;
|
||||
|
||||
// Overall UI style. "full" is normal, "light" is a single page focused, panels hidden experience.
|
||||
@@ -89,7 +88,7 @@ export interface IGristUrlState {
|
||||
billingPlan?: string;
|
||||
billingTask?: BillingTask;
|
||||
embed?: boolean;
|
||||
next?: string;
|
||||
state?: string;
|
||||
style?: InterfaceStyle;
|
||||
compare?: string;
|
||||
linkParameters?: Record<string, string>; // Parameters to pass as 'user.Link' in granular ACLs.
|
||||
@@ -302,13 +301,16 @@ export function decodeUrl(gristConfig: Partial<GristLoadConfig>, location: Locat
|
||||
|
||||
if (map.has('signup')) {
|
||||
state.login = 'signup';
|
||||
} else if (map.has('login')) {
|
||||
state.login = 'login';
|
||||
} else if (map.has('verified')) {
|
||||
state.login = 'verified';
|
||||
} else if (map.has('forgot-password')) {
|
||||
state.login = 'forgot-password';
|
||||
}
|
||||
|
||||
if (sp.has('next')) { state.params!.next = sp.get('next')!; }
|
||||
if (sp.has('state')) {
|
||||
state.params!.state = sp.get('state')!;
|
||||
}
|
||||
|
||||
if (sp.has('style')) {
|
||||
state.params!.style = InterfaceStyle.parse(sp.get('style'));
|
||||
@@ -407,6 +409,9 @@ export function parseSubdomain(host: string|undefined): {org?: string, base?: st
|
||||
return {};
|
||||
}
|
||||
|
||||
// Allowed localhost addresses.
|
||||
const localhostRegex = /^localhost(?::(\d+))?$/i;
|
||||
|
||||
/**
|
||||
* Like parseSubdomain, but throws an error if neither of these cases apply:
|
||||
* - host can be parsed into a valid subdomain and a valid base domain.
|
||||
@@ -566,7 +571,7 @@ export function isOrgInPathOnly(host?: string): boolean {
|
||||
const gristConfig: GristLoadConfig = (window as any).gristConfig;
|
||||
return (gristConfig && gristConfig.pathOnly) || false;
|
||||
} else {
|
||||
if (host && host.match(/^localhost(:[0-9]+)?$/)) { return true; }
|
||||
if (host && host.match(localhostRegex)) { return true; }
|
||||
return (process.env.GRIST_ORG_IN_PATH === 'true');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,3 +921,12 @@ export function getDistinctValues<T>(values: readonly T[], count: number = Infin
|
||||
}
|
||||
return distinct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that variable `name` has a non-nullish `value`.
|
||||
*/
|
||||
export function assertIsDefined<T>(name: string, value: T): asserts value is NonNullable<T> {
|
||||
if (value === undefined || value === null) {
|
||||
throw new Error(`Expected '${name}' to be defined, but received ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user