Skip to content

Commit a88bef3

Browse files
add user registration functionality with JWT authentication
1 parent c5c75a4 commit a88bef3

6 files changed

Lines changed: 120 additions & 4 deletions

File tree

client/src/pages/login.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Link from "next/link";
12
import { useState } from "react";
23

34
import { Button } from "@/components/ui/button";
@@ -37,7 +38,9 @@ export default function Login() {
3738
<CardDescription>
3839
Enter your email below to login to your account
3940
</CardDescription>
40-
<Button variant="link">Sign Up</Button>
41+
<Link href={"/register"}>
42+
<Button variant="link">Sign Up</Button>
43+
</Link>
4144
</CardHeader>
4245
<CardContent>
4346
<form>

client/src/pages/register.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { useState } from "react";
2+
3+
import { Button } from "@/components/ui/button";
4+
import {
5+
Card,
6+
CardContent,
7+
CardDescription,
8+
CardFooter,
9+
CardHeader,
10+
CardTitle,
11+
} from "@/components/ui/card";
12+
import { Input } from "@/components/ui/input";
13+
import api from "@/lib/api";
14+
15+
export default function Signup() {
16+
const [email, setEmail] = useState("");
17+
const [username, setUsername] = useState("");
18+
const [password, setPassword] = useState("");
19+
20+
//called from onclick submit to post variables for registration
21+
const register = async (
22+
email: string,
23+
username: string,
24+
password: string,
25+
) => {
26+
await api.post("/user/register/", {
27+
email,
28+
username,
29+
password,
30+
});
31+
};
32+
33+
return (
34+
<div className="flex min-h-screen items-center justify-center bg-muted">
35+
<Card className="w-full max-w-sm">
36+
<CardHeader>
37+
<CardTitle>Signup</CardTitle>
38+
<CardDescription>
39+
Enter your email below to login to your account
40+
</CardDescription>
41+
</CardHeader>
42+
<CardContent>
43+
<form>
44+
<div className="flex flex-col gap-6">
45+
<div className="grid gap-2">
46+
<Input
47+
id="email"
48+
type="email"
49+
placeholder="Example@mail.com"
50+
required
51+
onChange={(e) => setEmail(e.target.value)}
52+
/>
53+
</div>
54+
<div className="grid gap-2">
55+
<Input
56+
id="username"
57+
type="username"
58+
placeholder="Username"
59+
required
60+
onChange={(e) => setUsername(e.target.value)}
61+
/>
62+
</div>
63+
<div className="grid gap-2">
64+
<Input
65+
id="password"
66+
type="password"
67+
onChange={(e) => setPassword(e.target.value)}
68+
/>
69+
</div>
70+
</div>
71+
</form>
72+
</CardContent>
73+
<CardFooter className="flex-col gap-2">
74+
<Button
75+
type="submit"
76+
className="w-full"
77+
onClick={() => register(email, username, password)}
78+
>
79+
Signup
80+
</Button>
81+
</CardFooter>
82+
</Card>
83+
</div>
84+
);
85+
}

server/server/settings.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@
7777

7878
REST_FRAMEWORK = {
7979
"DEFAULT_AUTHENTICATION_CLASSES": (
80-
'rest_framework.authentication.SessionAuthentication',
8180
"rest_framework_simplejwt.authentication.JWTAuthentication",
8281
),
8382
}

server/user/serializers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,18 @@ class Meta:
2020
"is_staff",
2121
"is_superuser",
2222
"profile",)
23+
24+
class RegisterSerializer(serializers.ModelSerializer):
25+
password = serializers.CharField(write_only=True)
26+
27+
class Meta:
28+
model = User
29+
fields = ("username", "email", "password")
30+
31+
def create(self, validated_data):
32+
user = User.objects.create_user(
33+
username=validated_data["username"],
34+
email=validated_data.get("email", ""),
35+
password=validated_data["password"],
36+
)
37+
return user

server/user/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
path("", views.UserList.as_view(), name="user-list"),
77
path("profile/", views.UserProfileList.as_view(), name="profile-list"),
88
path("profile/<int:pk>/", views.UserProfileDetail.as_view(), name="user-profile-detail"),
9+
path("register/", views.RegisterView.as_view(), name="user-register"),
910
]
1011

server/user/views.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from django.shortcuts import render
2-
from rest_framework import generics, permissions
2+
from rest_framework import generics, permissions, status
33
from rest_framework.generics import ListAPIView
44
from rest_framework.response import Response
55
from rest_framework.views import APIView
66
from .permissions import IsUserOrReadOnly
7+
from rest_framework.permissions import AllowAny
78

89
from .models import Profile, User
9-
from .serializers import ProfileSerializer, UserSerializer
10+
from .serializers import ProfileSerializer, UserSerializer, RegisterSerializer
1011

1112
# Create your views here.
1213

@@ -25,3 +26,15 @@ class UserProfileDetail(generics.RetrieveUpdateAPIView):
2526
permission_classes = (permissions.IsAuthenticated, IsUserOrReadOnly)
2627
queryset = Profile.objects.all()
2728
serializer_class = ProfileSerializer
29+
30+
class RegisterView(APIView):
31+
authentication_classes = []
32+
permission_classes = [AllowAny]
33+
serializer_class = RegisterSerializer
34+
def post(self, request):
35+
serializer = RegisterSerializer(data=request.data)
36+
if serializer.is_valid():
37+
serializer.save()
38+
return Response(serializer.data, status=status.HTTP_201_CREATED)
39+
40+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

0 commit comments

Comments
 (0)