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

@@ -5,17 +5,17 @@ import type { Context } from "koa";
import Router from "koa-router";
import { Profile } from "passport";
import { slugifyDomain } from "@shared/utils/domains";
import accountProvisioner, {
AccountProvisionerResult,
} from "@server/commands/accountProvisioner";
import accountProvisioner from "@server/commands/accountProvisioner";
import env from "@server/env";
import { MicrosoftGraphError } from "@server/errors";
import passportMiddleware from "@server/middlewares/passport";
import { User } from "@server/models";
import { AuthenticationResult } from "@server/types";
import {
StateStore,
request,
getTeamFromContext,
getClientFromContext,
} from "@server/utils/passport";
const router = new Router();
@@ -49,7 +49,7 @@ if (env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET) {
done: (
err: Error | null,
user: User | null,
result?: AccountProvisionerResult
result?: AuthenticationResult
) => void
) {
try {
@@ -94,6 +94,7 @@ if (env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET) {
}
const team = await getTeamFromContext(ctx);
const client = getClientFromContext(ctx);
const domain = email.split("@")[1];
const subdomain = slugifyDomain(domain);
@@ -124,7 +125,7 @@ if (env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET) {
scopes,
},
});
return done(null, result.user, result);
return done(null, result.user, { ...result, client });
} catch (err) {
return done(err, null);
}

View File

@@ -1,5 +1,6 @@
import Router from "koa-router";
import { find } from "lodash";
import { Client } from "@shared/types";
import { parseDomain } from "@shared/utils/domains";
import { RateLimiterStrategy } from "@server/RateLimiter";
import InviteAcceptedEmail from "@server/emails/templates/InviteAcceptedEmail";
@@ -26,7 +27,7 @@ router.post(
errorHandling(),
rateLimiter(RateLimiterStrategy.TenPerHour),
async (ctx) => {
const { email } = ctx.request.body;
const { email, client } = ctx.request.body;
assertEmail(email, "email is required");
const domain = parseDomain(ctx.request.hostname);
@@ -81,6 +82,7 @@ router.post(
to: user.email,
token: user.getEmailSigninToken(),
teamUrl: team.url,
client: client === Client.Desktop ? Client.Desktop : Client.Web,
});
user.lastSigninEmailSentAt = new Date();
await user.save();
@@ -93,7 +95,7 @@ router.post(
);
router.get("email.callback", async (ctx) => {
const { token } = ctx.request.query;
const { token, client } = ctx.request.query;
assertPresent(token, "token is required");
let user!: User;
@@ -131,7 +133,13 @@ router.get("email.callback", async (ctx) => {
}
// set cookies on response and redirect to team subdomain
await signIn(ctx, user, user.team, "email", false, false);
await signIn(ctx, "email", {
user,
team: user.team,
isNewTeam: false,
isNewUser: false,
client: client === Client.Desktop ? Client.Desktop : Client.Web,
});
});
export default router;

View File

@@ -5,9 +5,7 @@ import { capitalize } from "lodash";
import { Profile } from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth2";
import { slugifyDomain } from "@shared/utils/domains";
import accountProvisioner, {
AccountProvisionerResult,
} from "@server/commands/accountProvisioner";
import accountProvisioner from "@server/commands/accountProvisioner";
import env from "@server/env";
import {
GmailAccountCreationError,
@@ -15,7 +13,12 @@ import {
} from "@server/errors";
import passportMiddleware from "@server/middlewares/passport";
import { User } from "@server/models";
import { StateStore, getTeamFromContext } from "@server/utils/passport";
import { AuthenticationResult } from "@server/types";
import {
StateStore,
getTeamFromContext,
getClientFromContext,
} from "@server/utils/passport";
const router = new Router();
const GOOGLE = "google";
@@ -58,13 +61,14 @@ if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
done: (
err: Error | null,
user: User | null,
result?: AccountProvisionerResult
result?: AuthenticationResult
) => void
) {
try {
// "domain" is the Google Workspaces domain
const domain = profile._json.hd;
const team = await getTeamFromContext(ctx);
const client = getClientFromContext(ctx);
// No profile domain means personal gmail account
// No team implies the request came from the apex domain
@@ -122,7 +126,7 @@ if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
},
});
return done(null, result.user, result);
return done(null, result.user, { ...result, client });
} catch (err) {
return done(err, null);
}

View File

@@ -4,9 +4,7 @@ import Router from "koa-router";
import { get } from "lodash";
import { Strategy } from "passport-oauth2";
import { slugifyDomain } from "@shared/utils/domains";
import accountProvisioner, {
AccountProvisionerResult,
} from "@server/commands/accountProvisioner";
import accountProvisioner from "@server/commands/accountProvisioner";
import env from "@server/env";
import {
OIDCMalformedUserInfoError,
@@ -14,10 +12,12 @@ import {
} from "@server/errors";
import passportMiddleware from "@server/middlewares/passport";
import { User } from "@server/models";
import { AuthenticationResult } from "@server/types";
import {
StateStore,
request,
getTeamFromContext,
getClientFromContext,
} from "@server/utils/passport";
const router = new Router();
@@ -73,7 +73,7 @@ if (env.OIDC_CLIENT_ID && env.OIDC_CLIENT_SECRET) {
done: (
err: Error | null,
user: User | null,
result?: AccountProvisionerResult
result?: AuthenticationResult
) => void
) {
try {
@@ -83,6 +83,7 @@ if (env.OIDC_CLIENT_ID && env.OIDC_CLIENT_SECRET) {
);
}
const team = await getTeamFromContext(ctx);
const client = getClientFromContext(ctx);
const parts = profile.email.toLowerCase().split("@");
const domain = parts.length && parts[1];
@@ -123,7 +124,7 @@ if (env.OIDC_CLIENT_ID && env.OIDC_CLIENT_SECRET) {
scopes,
},
});
return done(null, result.user, result);
return done(null, result.user, { ...result, client });
} catch (err) {
return done(err, null);
}

View File

@@ -3,9 +3,7 @@ import type { Context } from "koa";
import Router from "koa-router";
import { Profile } from "passport";
import { Strategy as SlackStrategy } from "passport-slack-oauth2";
import accountProvisioner, {
AccountProvisionerResult,
} from "@server/commands/accountProvisioner";
import accountProvisioner from "@server/commands/accountProvisioner";
import env from "@server/env";
import auth from "@server/middlewares/authentication";
import passportMiddleware from "@server/middlewares/passport";
@@ -16,7 +14,12 @@ import {
Team,
User,
} from "@server/models";
import { getTeamFromContext, StateStore } from "@server/utils/passport";
import { AuthenticationResult } from "@server/types";
import {
getClientFromContext,
getTeamFromContext,
StateStore,
} from "@server/utils/passport";
import * as Slack from "@server/utils/slack";
import { assertPresent, assertUuid } from "@server/validation";
@@ -80,11 +83,13 @@ if (env.SLACK_CLIENT_ID && env.SLACK_CLIENT_SECRET) {
done: (
err: Error | null,
user: User | null,
result?: AccountProvisionerResult
result?: AuthenticationResult
) => void
) {
try {
const team = await getTeamFromContext(ctx);
const client = getClientFromContext(ctx);
const result = await accountProvisioner({
ip: ctx.ip,
team: {
@@ -110,7 +115,7 @@ if (env.SLACK_CLIENT_ID && env.SLACK_CLIENT_SECRET) {
scopes,
},
});
return done(null, result.user, result);
return done(null, result.user, { ...result, client });
} catch (err) {
return done(err, null);
}