Skip to content

Commit

Permalink
fix: remove anonymous guest user (#432)
Browse files Browse the repository at this point in the history
  • Loading branch information
lihebi authored Aug 4, 2023
1 parent b77c374 commit cc917b4
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 150 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Warnings:
- You are about to drop the column `isGuest` on the `User` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "User" DROP COLUMN "isGuest";
1 change: 0 additions & 1 deletion api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ model User {
lastname String
// A user might not have a password, if they login via OAuth.
hashedPassword String?
isGuest Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
Expand Down
46 changes: 1 addition & 45 deletions api/src/resolver_user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { OAuth2Client } from "google-auth-library";
import { customAlphabet } from "nanoid/async";
import { lowercase, numbers } from "nanoid-dictionary";

import prisma from './client'
import prisma from "./client";

const nanoid = customAlphabet(lowercase + numbers, 20);

Expand Down Expand Up @@ -40,48 +40,6 @@ async function signup(_, { email, password, firstname, lastname }) {
};
}

/**
* Create a guest user and return a token. The guest user doesn't have a password or email.
*/
async function signupGuest(_, {}) {
const id = await nanoid();
const user = await prisma.user.create({
data: {
id: id,
email: id + "@example.com",
firstname: "Guest",
lastname: "Guest",
isGuest: true,
},
});
return {
// CAUTION the front-end should save the user ID so that we can login again after expiration.
token: jwt.sign({ id: user.id }, process.env.JWT_SECRET as string, {
expiresIn: "30d",
}),
};
}

/**
* Login a user with a guest ID and no password.
*/
async function loginGuest(_, { id }) {
const user = await prisma.user.findFirst({
where: {
id,
isGuest: true,
},
});
if (!user) throw Error(`User does not exist`);
return {
id: user.id,
email: user.email,
token: jwt.sign({ id: user.id }, process.env.JWT_SECRET as string, {
expiresIn: "30d",
}),
};
}

async function updateUser(_, { email, firstname, lastname }, { userId }) {
if (!userId) throw Error("Unauthenticated");
let user = await prisma.user.findFirst({
Expand Down Expand Up @@ -201,8 +159,6 @@ export default {
loginWithGoogle,
signup,
updateUser,
signupGuest,
loginGuest,
updateCodeiumAPIKey,
},
};
2 changes: 0 additions & 2 deletions api/src/typedefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@ export const typeDefs = gql`
type Mutation {
login(email: String!, password: String!): AuthData
loginGuest(id: String!): AuthData
signupGuest: AuthData
signup(
email: String!
password: String!
Expand Down
76 changes: 3 additions & 73 deletions ui/src/lib/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@ function useProvideAuth() {

useEffect(() => {
// load initial state from local storage
setAuthToken(
localStorage.getItem("token") ||
localStorage.getItem("guestToken") ||
null
);
setAuthToken(localStorage.getItem("token") || null);
}, []);

const getAuthHeaders = (): Record<string, string> => {
Expand Down Expand Up @@ -65,7 +61,7 @@ function useProvideAuth() {
// HEBI CAUTION this must be removed. Otherwise, when getItem back, it is not null, but "null"
// localStorage.setItem("token", null);
localStorage.removeItem("token");
setAuthToken(localStorage.getItem("guestToken") || null);
setAuthToken(null);
};

const handleGoogle = async (response) => {
Expand All @@ -91,68 +87,6 @@ function useProvideAuth() {
}
};

let guestSigningUp = false;

const loginGuest = async () => {
console.log("Loginning as guest.");
// If there is a guest token, decode the guest ID from it, and login with the guest ID
let token = localStorage.getItem("guestToken");
if (token) {
console.log("Guest token found, logining in ..");
const { id } = jwt_decode(token) as { id: string };
// login a guest user with the guest ID
const client = createApolloClient();
const LoginGuestMutation = gql`
mutation LoginGuestMutation($id: String!) {
loginGuest(id: $id) {
token
}
}
`;
const result = await client.mutate({
mutation: LoginGuestMutation,
variables: { id },
});
if (result?.data?.loginGuest?.token) {
const token = result.data.loginGuest.token;
setAuthToken(token);
localStorage.setItem("guestToken", token);
}
} else {
// Signup a guest user
console.log("Guest token not found, signing up ..");
// set a 5 seconds timeout so that no duplicate guest users are created
if (guestSigningUp) {
console.log("Guest signing up, waiting ..");
return;
}
guestSigningUp = true;
setTimeout(() => {
guestSigningUp = false;
}, 5000);

// actually signup the user
const client = createApolloClient();
const SignupGuestMutation = gql`
mutation SignupGuestMutation {
signupGuest {
token
}
}
`;

const result = await client.mutate({
mutation: SignupGuestMutation,
});

if (result?.data?.signupGuest?.token) {
const token = result.data.signupGuest.token;
setAuthToken(token);
localStorage.setItem("guestToken", token);
}
}
};

const signIn = async ({ email, password }) => {
const client = createApolloClient(false);
const LoginMutation = gql`
Expand Down Expand Up @@ -231,17 +165,13 @@ function useProvideAuth() {
* This is set immediately on refresh.
*/
function hasToken() {
return (
localStorage.getItem("token") !== null ||
localStorage.getItem("guestToken") !== null
);
return localStorage.getItem("token") !== null;
}

return {
createApolloClient,
signIn,
signOut,
loginGuest,
handleGoogle,
signUp,
isSignedIn,
Expand Down
64 changes: 37 additions & 27 deletions ui/src/pages/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -417,18 +417,48 @@ const RepoLists = () => {
);
};

export default function Dashboard() {
const { me } = useMe();
const { hasToken, loginGuest, isSignedIn } = useAuth();
function NoLogginErrorAlert() {
const nevigate = useNavigate();
const [seconds, setSeconds] = useState<number | null>(3);

useEffect(() => {
if (!hasToken()) {
loginGuest();
if (seconds === 0) {
setSeconds(null);
nevigate("/login");
return;
}
}, [hasToken]);
if (seconds === null) return;

const timer = setTimeout(() => {
setSeconds((prev) => prev! - 1);
}, 1000);

return () => clearTimeout(timer);
}, [nevigate, seconds]);

return (
<Box sx={{ maxWidth: "sm", alignItems: "center", m: "auto" }}>
<Alert severity="error">
Please login first! Automatically jump to{" "}
<Link component={ReactLink} to="/login">
login
</Link>{" "}
page in {seconds} seconds.
</Alert>
</Box>
);
}

export default function Dashboard() {
const { loading, me } = useMe();
if (loading)
return (
<Box sx={{ maxWidth: "md", alignItems: "center", m: "auto" }}>
Loading ..
</Box>
);
if (!me) {
return <Box>Loading user ..</Box>;
return <NoLogginErrorAlert />;
}
return (
<Box sx={{ maxWidth: "md", alignItems: "center", m: "auto" }}>
Expand All @@ -443,26 +473,6 @@ export default function Dashboard() {
Welcome, {me?.firstname}! Please open or create a repository to get
started.
</Box>
{!isSignedIn() && (
<Box sx={{ alignItems: "center", m: "auto" }}>
<Alert severity="warning">
Please note that you are a{" "}
<Box component="span" color="red">
Guest
</Box>{" "}
user. Please{" "}
<Link component={ReactLink} to="/login">
Login
</Link>{" "}
or
<Link component={ReactLink} to="/signup">
Signup
</Link>{" "}
to save your work.
<GoogleSignin />
</Alert>
</Box>
)}
<RepoLists />
</Box>
);
Expand Down
6 changes: 4 additions & 2 deletions website/docs/3-manual/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

**Try it [here (experimental)](https://app.codepod.io)**

## Optional User Registration
## Signup & Login

Codepod is a web app with standard user authentication. You can register, or better, you can login with your google account instantly. If you don't register & login, the system will register a Guest user, so that people can try out Codepod without registering.
Codepod is a web application that offers standard user authentication. Moreover,
you can experience the convenience of instant access by logging in with your
Google account.

<img src={require("./assets/signup.png").default} alt="xxx" width="600" />

Expand Down

0 comments on commit cc917b4

Please sign in to comment.