Files
webstudio/apps/builder/app/services/auth.server.ts
T
Ivan Starkov 3105b5cec0 feat: Make it easy for a self-hosted version to set the plan features (#3475)
## Description

closes #3400 


`+` on dev login its now possible to add any email
like 
`{code}:hello@world.xx` to login as other user


## Steps for reproduction

Login on main with `{code}:ice@yandex.ru`
- See no plan
<img width="213" alt="image"
src="https://github.com/webstudio-is/webstudio/assets/5077042/65f79295-63b3-4d4f-b5d2-ed854f8462e3">

---

Login on main as usual
- See user has plan
<img width="265" alt="image"
src="https://github.com/webstudio-is/webstudio/assets/5077042/db9e1c3e-a8d7-4cde-bbd5-1f39ce7442b8">

---

Login locally
See user has plan

<img width="231" alt="image"
src="https://github.com/webstudio-is/webstudio/assets/5077042/c1187bce-2c7e-49f3-a3ac-bfa15560ccce">




## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")

## Before merging

- [ ] tested locally and on preview environment (preview dev login:
5de6)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env.example`
and the `builder/env-check.js` if mandatory
2024-06-02 14:54:19 +03:00

115 lines
3.0 KiB
TypeScript

import { Authenticator } from "remix-auth";
import { FormStrategy } from "remix-auth-form";
import { GitHubStrategy, type GitHubProfile } from "remix-auth-github";
import { GoogleStrategy, type GoogleProfile } from "remix-auth-google";
import * as db from "~/shared/db";
import { sessionStorage } from "~/services/session.server";
import { AUTH_PROVIDERS } from "~/shared/session";
import { authCallbackPath } from "~/shared/router-utils";
import { getUserById, type User } from "~/shared/db/user.server";
import env from "~/env/env.server";
const url =
env.DEPLOYMENT_ENVIRONMENT === "production"
? env.DEPLOYMENT_URL
: `http://localhost:${env.PORT || 3000}`;
const strategyCallback = async ({
profile,
}: {
profile: GitHubProfile | GoogleProfile;
}) => {
try {
const user = await db.user.createOrLoginWithOAuth(profile);
return user;
} catch (error) {
if (error instanceof Error) {
console.error({
error,
extras: {
loginMethod: AUTH_PROVIDERS.LOGIN_DEV,
},
});
}
throw error;
}
};
// Create an instance of the authenticator, pass a generic with what
// strategies will return and will store in the session
export const authenticator = new Authenticator<User>(sessionStorage, {
throwOnError: true,
});
if (env.GH_CLIENT_ID && env.GH_CLIENT_SECRET) {
const github = new GitHubStrategy(
{
clientID: env.GH_CLIENT_ID,
clientSecret: env.GH_CLIENT_SECRET,
callbackURL: `${url}${authCallbackPath({ provider: "github" })}`,
},
strategyCallback
);
authenticator.use(github, "github");
}
if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
const google = new GoogleStrategy(
{
clientID: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
callbackURL: `${url}${authCallbackPath({ provider: "google" })}`,
},
strategyCallback
);
authenticator.use(google, "google");
}
if (env.DEV_LOGIN === "true") {
authenticator.use(
new FormStrategy(async ({ form }) => {
const secretValue = form.get("secret");
if (secretValue == null) {
throw new Error("Secret is required");
}
const [secret, email = "hello@webstudio.is"] = secretValue
.toString()
.split(":");
if (secret === env.AUTH_SECRET?.slice(0, 4)) {
try {
const user = await db.user.createOrLoginWithDev(email);
return user;
} catch (error) {
if (error instanceof Error) {
console.error({
error,
extras: {
loginMethod: AUTH_PROVIDERS.LOGIN_DEV,
},
});
}
throw error;
}
}
throw new Error("Secret is incorrect");
}),
"dev"
);
}
export const findAuthenticatedUser = async (request: Request) => {
const user = await authenticator.isAuthenticated(request);
if (user == null) {
return null;
}
try {
return await getUserById(user.id);
} catch (error) {
return null;
}
};