Email verification frontend.
This commit is contained in:
@@ -5,7 +5,7 @@ import { ConfigModule } from '@nestjs/config';
|
|||||||
import { GraphQLISODateTime, GraphQLModule } from '@nestjs/graphql';
|
import { GraphQLISODateTime, GraphQLModule } from '@nestjs/graphql';
|
||||||
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||||
import { ApolloServerPluginLandingPageLocalDefault } from 'apollo-server-core';
|
import { ApolloServerPluginLandingPageLocalDefault } from 'apollo-server-core';
|
||||||
import { UsersModule } from './users/users.module';
|
// import { UsersModule } from './users/users.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -29,7 +29,7 @@ import { UsersModule } from './users/users.module';
|
|||||||
resolvers: { DateTime: GraphQLISODateTime },
|
resolvers: { DateTime: GraphQLISODateTime },
|
||||||
plugins: [ApolloServerPluginLandingPageLocalDefault()],
|
plugins: [ApolloServerPluginLandingPageLocalDefault()],
|
||||||
}),
|
}),
|
||||||
UsersModule,
|
// UsersModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|||||||
@@ -46,15 +46,32 @@ export class SupertokensService {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
Session
|
Session.init({
|
||||||
.init
|
// antiCsrf: "VIA_CUSTOM_HEADER",
|
||||||
// {
|
jwt: {
|
||||||
// jwt: {
|
enable: true,
|
||||||
// enable: true,
|
issuer: `${process.env.APP_URL}/auth/api`,
|
||||||
// issuer: `${process.env.APP_URL}/auth/api`,
|
},
|
||||||
// },
|
override: {
|
||||||
// }
|
functions: function (originalImplementation) {
|
||||||
(),
|
return {
|
||||||
|
...originalImplementation,
|
||||||
|
createNewSession: async function (input) {
|
||||||
|
input.accessTokenPayload = {
|
||||||
|
...input.accessTokenPayload,
|
||||||
|
'https://hasura.io/jwt/claims': {
|
||||||
|
'x-hasura-user-id': input.userId,
|
||||||
|
'x-hasura-default-role': 'user',
|
||||||
|
'x-hasura-allowed-roles': ['user'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return originalImplementation.createNewSession(input);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,18 @@ import { NestFactory } from '@nestjs/core';
|
|||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import supertokens from 'supertokens-node';
|
import supertokens from 'supertokens-node';
|
||||||
import { SupertokensExceptionFilter } from './auth/filters/auth.filter';
|
import { SupertokensExceptionFilter } from './auth/filters/auth.filter';
|
||||||
import { PrismaService } from 'prisma/prisma.service';
|
// import { PrismaService } from 'prisma/prisma.service';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: [process.env.WEBAPP_URL, 'https://studio.apollographql.com'],
|
origin: [process.env.WEBAPP_URL, 'http://server.home:8081'],
|
||||||
allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()],
|
allowedHeaders: ['content-type', ...supertokens.getAllCORSHeaders()],
|
||||||
credentials: true,
|
credentials: true,
|
||||||
});
|
});
|
||||||
app.useGlobalFilters(new SupertokensExceptionFilter());
|
app.useGlobalFilters(new SupertokensExceptionFilter());
|
||||||
const prismaService = app.get(PrismaService);
|
// const prismaService = app.get(PrismaService);
|
||||||
await prismaService.enableShutdownHooks(app);
|
// await prismaService.enableShutdownHooks(app);
|
||||||
|
|
||||||
await app.listen(process.env.APP_PORT);
|
await app.listen(process.env.APP_PORT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ import { UsersService } from './users.service';
|
|||||||
export class UsersResolver {
|
export class UsersResolver {
|
||||||
constructor(private readonly usersService: UsersService) {}
|
constructor(private readonly usersService: UsersService) {}
|
||||||
|
|
||||||
@Mutation('createUser')
|
// @Mutation('createUser')
|
||||||
create(@Args('createUserInput') createUserInput: Prisma.UserCreateInput) {
|
// create(@Args('createUserInput') createUserInput: Prisma.UserCreateInput) {
|
||||||
return this.usersService.create(createUserInput);
|
// return this.usersService.create(createUserInput);
|
||||||
}
|
// }
|
||||||
@Query('users')
|
// @Query('users')
|
||||||
findAll(@Args('params') params?: Prisma.UserFindManyArgs) {
|
// findAll(@Args('params') params?: Prisma.UserFindManyArgs) {
|
||||||
return this.usersService.users(params);
|
// return this.usersService.users(params);
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Query('user')
|
// @Query('user')
|
||||||
findOne(@Args('id') id: string) {
|
// findOne(@Args('id') id: string) {
|
||||||
return this.usersService.user({ id });
|
// return this.usersService.user({ id });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// @Mutation('updateUser')
|
// @Mutation('updateUser')
|
||||||
// update(@Args('updateUserInput') updateUserInput: UpdateUserInput) {
|
// update(@Args('updateUserInput') updateUserInput: UpdateUserInput) {
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Prisma, User } from '@prisma/client';
|
// import { Prisma, User } from '@prisma/client';
|
||||||
import { PrismaService } from 'prisma/prisma.service';
|
import { PrismaService } from 'prisma/prisma.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
constructor(private readonly prismaService: PrismaService) {}
|
constructor(private readonly prismaService: PrismaService) {}
|
||||||
|
|
||||||
async user(uniqueInput: Prisma.UserWhereUniqueInput) {
|
// async user(uniqueInput: Prisma.UserWhereUniqueInput) {
|
||||||
return await this.prismaService.user.findUnique({ where: uniqueInput });
|
// return await this.prismaService.user.findUnique({ where: uniqueInput });
|
||||||
}
|
// }
|
||||||
|
|
||||||
async users(params?: Prisma.UserFindManyArgs) {
|
// async users(params?: Prisma.UserFindManyArgs) {
|
||||||
return await this.prismaService.user.findMany(params);
|
// return await this.prismaService.user.findMany(params);
|
||||||
}
|
// }
|
||||||
|
|
||||||
create(createUserInput: Prisma.UserCreateInput) {
|
// create(createUserInput: Prisma.UserCreateInput) {
|
||||||
return this.prismaService.user.create({ data: createUserInput });
|
// return this.prismaService.user.create({ data: createUserInput });
|
||||||
}
|
// }
|
||||||
|
|
||||||
update(id: string, userUpdateInput: Prisma.UserUpdateInput) {
|
// update(id: string, userUpdateInput: Prisma.UserUpdateInput) {
|
||||||
return this.prismaService.user.update({
|
// return this.prismaService.user.update({
|
||||||
where: { id },
|
// where: { id },
|
||||||
data: userUpdateInput,
|
// data: userUpdateInput,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// remove(id: string) {
|
// remove(id: string) {
|
||||||
// return `This action removes a #${id} user`;
|
// return `This action removes a #${id} user`;
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ SuperTokens.init({
|
|||||||
apiBasePath: "/auth/api",
|
apiBasePath: "/auth/api",
|
||||||
appName: "Fluxem",
|
appName: "Fluxem",
|
||||||
},
|
},
|
||||||
recipeList: [EmailPass.init(), Session.init()],
|
recipeList: [
|
||||||
|
EmailPass.init(),
|
||||||
|
Session.init(),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const App: Component = () => {
|
const App: Component = () => {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createSignal } from "solid-js";
|
|||||||
import { createStore } from "solid-js/store";
|
import { createStore } from "solid-js/store";
|
||||||
|
|
||||||
import EmailPassRecipe from "supertokens-web-js/recipe/emailpassword";
|
import EmailPassRecipe from "supertokens-web-js/recipe/emailpassword";
|
||||||
|
import Session from "supertokens-web-js/recipe/session";
|
||||||
|
|
||||||
const useLogin = () => {
|
const useLogin = () => {
|
||||||
const [loading, setLoading] = createSignal(false);
|
const [loading, setLoading] = createSignal(false);
|
||||||
@@ -22,7 +23,7 @@ const useLogin = () => {
|
|||||||
{ id: "password", value: form.password },
|
{ id: "password", value: form.password },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
console.log(login);
|
console.log(login)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -30,7 +31,26 @@ const useLogin = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { handleInput, loading, handleLogin, form };
|
const handleLogout = async () => {
|
||||||
|
const logout = await EmailPassRecipe.signOut();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleJwt = async () => {
|
||||||
|
const session = await Session.doesSessionExist();
|
||||||
|
if (session) {
|
||||||
|
let userId = await Session.getUserId();
|
||||||
|
let jwt = (await Session.getAccessTokenPayloadSecurely()).jwt;
|
||||||
|
// console.log(userId, jwt);
|
||||||
|
const {isVerified} = await EmailPassRecipe.isEmailVerified();
|
||||||
|
console.log(isVerified)
|
||||||
|
if(!isVerified) {
|
||||||
|
const sendEmail = await EmailPassRecipe.sendVerificationEmail()
|
||||||
|
console.log(sendEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { handleInput, loading, handleLogin, handleLogout, handleJwt, form };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useLogin;
|
export default useLogin;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const Home = lazy(() => import("./views/home/Home"));
|
|||||||
const AuthLayout = lazy(() => import("./views/auth/AuthLayout"));
|
const AuthLayout = lazy(() => import("./views/auth/AuthLayout"));
|
||||||
const Login = lazy(() => import("./views/auth/Login"));
|
const Login = lazy(() => import("./views/auth/Login"));
|
||||||
const Register = lazy(() => import("./views/auth/Register"));
|
const Register = lazy(() => import("./views/auth/Register"));
|
||||||
|
const VerifyEmail = lazy(() => import("./views/auth/VerifyEmail"));
|
||||||
const AppRouter: Component = () => {
|
const AppRouter: Component = () => {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
@@ -13,6 +13,7 @@ const AppRouter: Component = () => {
|
|||||||
<Route path="/auth" element={<AuthLayout />}>
|
<Route path="/auth" element={<AuthLayout />}>
|
||||||
<Route path="/login" element={<Login />}></Route>
|
<Route path="/login" element={<Login />}></Route>
|
||||||
<Route path="/register" element={<Register />}></Route>
|
<Route path="/register" element={<Register />}></Route>
|
||||||
|
<Route path="/verify-email" element={<VerifyEmail />}></Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Show } from "solid-js";
|
import { Component, Show } from "solid-js";
|
||||||
import useLogin from "../../../hooks/auth/login.hook";
|
import useLogin from "../../../hooks/auth/login.hook";
|
||||||
const { handleLogin, handleInput, loading, form } = useLogin();
|
const { handleLogin, handleLogout, handleInput, handleJwt, loading, form } = useLogin();
|
||||||
const Login: Component = () => {
|
const Login: Component = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -32,6 +32,12 @@ const Login: Component = () => {
|
|||||||
</Show>
|
</Show>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
<button type="button" onclick={handleLogout}>
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
<button type="button" onclick={handleJwt}>
|
||||||
|
GetJWT
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
18
frontend/src/routes/views/auth/VerifyEmail.tsx
Normal file
18
frontend/src/routes/views/auth/VerifyEmail.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import EmailPassRecipe from "supertokens-web-js/recipe/emailpassword";
|
||||||
|
|
||||||
|
const VerifyEmail = () => {
|
||||||
|
let text = "Verifying email";
|
||||||
|
const verifyEmail = EmailPassRecipe.verifyEmail();
|
||||||
|
verifyEmail.then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
text = "Email verified successfully";
|
||||||
|
});
|
||||||
|
// console.log(verifyEmail);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>{text}</h2>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VerifyEmail;
|
||||||
Reference in New Issue
Block a user