Desktop support (#4484)

* Remove home link on desktop app

* Spellcheck, installation toasts, background styling, …

* Add email,slack, auth support

* More desktop style tweaks

* Move redirect to client

* cleanup

* Record desktop usage

* docs

* fix: Selection state in search input when double clicking header
This commit is contained in:
Tom Moor
2022-11-27 15:07:48 -08:00
committed by GitHub
parent ea9680c3d7
commit cc333637dd
38 changed files with 492 additions and 83 deletions

View File

@@ -2,10 +2,12 @@ import querystring from "querystring";
import { addMonths } from "date-fns";
import { Context } from "koa";
import { pick } from "lodash";
import { Client } from "@shared/types";
import { getCookieDomain } from "@shared/utils/domains";
import env from "@server/env";
import Logger from "@server/logging/Logger";
import { User, Event, Team, Collection, View } from "@server/models";
import { Event, Collection, View } from "@server/models";
import { AuthenticationResult } from "@server/types";
/**
* Parse and return the details from the "sessions" cookie in the request, if
@@ -27,11 +29,8 @@ export function getSessionsInCookie(ctx: Context) {
export async function signIn(
ctx: Context,
user: User,
team: Team,
service: string,
_isNewUser = false,
isNewTeam = false
{ user, team, client, isNewTeam }: AuthenticationResult
) {
if (user.isSuspended) {
return ctx.redirect("/?notice=suspended");
@@ -74,6 +73,7 @@ export async function signIn(
});
const domain = getCookieDomain(ctx.request.hostname);
const expires = addMonths(new Date(), 3);
// set a cookie for which service we last signed in with. This is
// only used to display a UI hint for the user for next time
ctx.cookies.set("lastSignedIn", service, {
@@ -103,7 +103,20 @@ export async function signIn(
expires,
domain,
});
ctx.redirect(`${team.url}/auth/redirect?token=${user.getTransferToken()}`);
// If the authentication request originally came from the desktop app then we send the user
// back to a screen in the web app that will immediately redirect to the desktop. The reason
// to do this from the client is that if you redirect from the server then the browser ends up
// stuck on the SSO screen.
if (client === Client.Desktop) {
ctx.redirect(
`${team.url}/desktop-redirect?token=${user.getTransferToken()}`
);
} else {
ctx.redirect(
`${team.url}/auth/redirect?token=${user.getTransferToken()}`
);
}
} else {
ctx.cookies.set("accessToken", user.getJwtToken(), {
sameSite: true,
@@ -136,6 +149,7 @@ export async function signIn(
}),
]);
const hasViewedDocuments = !!view;
ctx.redirect(
!hasViewedDocuments && collection
? `${team.url}${collection.url}`

View File

@@ -6,6 +6,7 @@ import {
StateStoreStoreCallback,
StateStoreVerifyCallback,
} from "passport-oauth2";
import { Client } from "@shared/types";
import { getCookieDomain, parseDomain } from "@shared/utils/domains";
import env from "@server/env";
import { Team } from "@server/models";
@@ -20,8 +21,10 @@ export class StateStore {
// We expect host to be a team subdomain, custom domain, or apex domain
// that is passed via query param from the auth provider component.
const clientInput = ctx.query.client?.toString();
const client = clientInput === Client.Desktop ? Client.Desktop : Client.Web;
const host = ctx.query.host?.toString() || parseDomain(ctx.hostname).host;
const state = buildState(host, token);
const state = buildState(host, token, client);
ctx.cookies.set(this.key, state, {
httpOnly: false,
@@ -76,13 +79,19 @@ export async function request(endpoint: string, accessToken: string) {
return response.json();
}
function buildState(host: string, token: string) {
return [host, token].join("|");
function buildState(host: string, token: string, client?: Client) {
return [host, token, client].join("|");
}
export function parseState(state: string) {
const [host, token] = state.split("|");
return { host, token };
const [host, token, client] = state.split("|");
return { host, token, client };
}
export function getClientFromContext(ctx: Context): Client {
const state = ctx.cookies.get("state");
const client = state ? parseState(state).client : undefined;
return client === Client.Desktop ? Client.Desktop : Client.Web;
}
export async function getTeamFromContext(ctx: Context) {
@@ -90,7 +99,6 @@ export async function getTeamFromContext(ctx: Context) {
// we use it to infer the team they intend on signing into
const state = ctx.cookies.get("state");
const host = state ? parseState(state).host : ctx.hostname;
const domain = parseDomain(host);
let team;