(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:
George Gevoian
2022-04-01 14:31:24 -07:00
parent 8fdfb02646
commit 6305811ca6
24 changed files with 188 additions and 185 deletions

View File

@@ -24,7 +24,8 @@
*/
import {unsavedChanges} from 'app/client/components/UnsavedChanges';
import {UrlState} from 'app/client/lib/UrlState';
import {decodeUrl, encodeUrl, getSlugIfNeeded, GristLoadConfig, IGristUrlState} from 'app/common/gristUrls';
import {decodeUrl, encodeUrl, getSlugIfNeeded, GristLoadConfig, IGristUrlState,
parseFirstUrlPart} from 'app/common/gristUrls';
import {addOrgToPath} from 'app/common/urlUtils';
import {Document} from 'app/common/UserAPI';
import isEmpty = require('lodash/isEmpty');
@@ -64,39 +65,40 @@ export function getMainOrgUrl(): string { return urlState().makeUrl({}); }
// When on a document URL, returns the URL with just the doc ID, omitting other bits (like page).
export function getCurrentDocUrl(): string { return urlState().makeUrl({docPage: undefined}); }
// Get url for the login page, which will then redirect to nextUrl (current page by default).
// Get url for the login page, which will then redirect to `nextUrl` (current page by default).
export function getLoginUrl(nextUrl: string | null = _getCurrentUrl()): string {
return _getLoginLogoutUrl('login', nextUrl ?? undefined);
return _getLoginLogoutUrl('login', nextUrl);
}
// Get url for the signup page, which will then redirect to nextUrl (current page by default).
// Get url for the signup page, which will then redirect to `nextUrl` (current page by default).
export function getSignupUrl(nextUrl: string = _getCurrentUrl()): string {
return _getLoginLogoutUrl('signup', nextUrl);
}
// Get url for the logout page, which will then redirect to nextUrl (signed-out page by default).
export function getLogoutUrl(nextUrl: string = getSignedOutUrl()): string {
return _getLoginLogoutUrl('logout', nextUrl);
// Get url for the logout page.
export function getLogoutUrl(): string {
return _getLoginLogoutUrl('logout');
}
// Get url for the login page, which will then redirect to nextUrl (current page by default).
// Get url for the signin page, which will then redirect to `nextUrl` (current page by default).
export function getLoginOrSignupUrl(nextUrl: string = _getCurrentUrl()): string {
return _getLoginLogoutUrl('signin', nextUrl);
}
// Returns the URL for the "you are signed out" page.
export function getSignedOutUrl(): string { return getMainOrgUrl() + "signed-out"; }
// Helper which returns the URL of the current page, except when it's the "/signed-out" page, in
// which case returns the org URL. This is a good URL to use for a post-login redirect.
// Returns the relative URL (i.e. path) of the current page, except when it's the
// "/signed-out" page, in which case it returns the home page ("/").
// This is a good URL to use for a post-login redirect.
function _getCurrentUrl(): string {
return window.location.pathname.endsWith("/signed-out") ? getMainOrgUrl() : window.location.href;
if (window.location.pathname.endsWith('/signed-out')) { return '/'; }
const {pathname, search} = new URL(window.location.href);
return parseFirstUrlPart('o', pathname).path + search;
}
// Helper for getLoginUrl()/getLogoutUrl().
function _getLoginLogoutUrl(method: 'login'|'logout'|'signin'|'signup', nextUrl?: string): string {
// Returns the URL for the given login page, with 'next' param optionally set.
function _getLoginLogoutUrl(page: 'login'|'logout'|'signin'|'signup', nextUrl?: string | null): string {
const startUrl = new URL(window.location.href);
startUrl.pathname = addOrgToPath('', window.location.href, true) + '/' + method;
startUrl.pathname = addOrgToPath('', window.location.href, true) + '/' + page;
if (nextUrl) { startUrl.searchParams.set('next', nextUrl); }
return startUrl.href;
}

View File

@@ -1,7 +1,7 @@
import {beaconOpenMessage} from 'app/client/lib/helpScout';
import {AppModel, reportError} from 'app/client/models/AppModel';
import {BillingModel, BillingModelImpl, ISubscriptionModel} from 'app/client/models/BillingModel';
import {getLoginUrl, getMainOrgUrl, urlState} from 'app/client/models/gristUrlState';
import {getLoginUrl, urlState} from 'app/client/models/gristUrlState';
import {AppHeader} from 'app/client/ui/AppHeader';
import {BillingForm, IFormData} from 'app/client/ui/BillingForm';
import * as css from 'app/client/ui/BillingPageCss';
@@ -499,7 +499,7 @@ export class BillingPage extends Disposable {
// If the user is not logged in and selects the free plan, provide a login link that
// redirects back to the free org.
return css.upgradeBtn('Sign up',
{href: getLoginUrl(getMainOrgUrl())},
{href: getLoginUrl()},
testId('plan-btn')
);
} else if ((!selectedPlan && plan.amount === 0) || (selectedPlan && plan.id === selectedPlan.id)) {

View File

@@ -98,7 +98,7 @@ export class WelcomePage extends Disposable {
`If you already have a Grist account as `,
dom('b', email.get()),
` you can just `,
dom('a', {href: getLoginUrl(urlState().makeUrl({}))}, 'log in'),
dom('a', {href: getLoginUrl()}, 'log in'),
` now. Otherwise, please pick a password.`
),
cssSeparatedLabel('The email address you activated Grist with:'),