diff --git a/auth_remindme/settings.py b/auth_remindme/settings.py
index b569ebce9d85cee48fb7ea7033c3abacbe70bf2e..3b79de802c31726234c39583b4bcca18f00a78cc 100644
--- a/auth_remindme/settings.py
+++ b/auth_remindme/settings.py
@@ -11,8 +11,8 @@ https://docs.djangoproject.com/en/4.0/ref/settings/
 """
 
 import os
+from datetime import timedelta
 from pathlib import Path
-
 import dj_database_url
 
 # Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -56,6 +56,8 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'main',
+    'oauth',
+    'rest_framework_simplejwt',
 ]
 
 MIDDLEWARE = [
@@ -164,3 +166,43 @@ for directory in [*STATICFILES_DIRS, STATIC_ROOT]:
 # Enable compression and caching features of whitenoise.
 # You can remove this if it causes problems on your setup.
 STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
+
+REST_FRAMEWORK = {
+    'DEFAULT_AUTHENTICATION_CLASSES': (
+        'rest_framework_simplejwt.authentication.JWTAuthentication',
+    )
+}
+
+AUTH_USER_MODEL = "oauth.UserAccount"
+
+SIMPLE_JWT = {
+    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
+    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
+    'ROTATE_REFRESH_TOKENS': False,
+    'BLACKLIST_AFTER_ROTATION': False,
+    'UPDATE_LAST_LOGIN': False,
+
+    'ALGORITHM': 'HS256',
+    'SIGNING_KEY': SECRET_KEY,
+    'VERIFYING_KEY': None,
+    'AUDIENCE': None,
+    'ISSUER': None,
+    'JWK_URL': None,
+    'LEEWAY': 0,
+
+    'AUTH_HEADER_TYPES': ('Bearer', 'JWT',),
+    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
+    'USER_ID_FIELD': 'id',
+    'USER_ID_CLAIM': 'user_id',
+    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
+
+    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
+    'TOKEN_TYPE_CLAIM': 'token_type',
+    'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
+
+    'JTI_CLAIM': 'jti',
+
+    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
+    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
+    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
+}
\ No newline at end of file
diff --git a/auth_remindme/urls.py b/auth_remindme/urls.py
index 3178999c40a6e11c697a51c8b3340273be791365..479ec1eee0cd064de7ab312099f4483f45a71392 100644
--- a/auth_remindme/urls.py
+++ b/auth_remindme/urls.py
@@ -19,4 +19,5 @@ from django.urls import include, path
 urlpatterns = [
     path('admin/', admin.site.urls),
     path('', include('main.urls')),
+    path('', include('oauth.urls'))
 ]
diff --git a/oauth/__init__.py b/oauth/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/oauth/admin.py b/oauth/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7e6cde0f26f2d98ad15b2c7c292784decc9ac31
--- /dev/null
+++ b/oauth/admin.py
@@ -0,0 +1,4 @@
+from django.contrib import admin
+from .models import UserAccount
+# Register your models here.
+admin.site.register(UserAccount)
\ No newline at end of file
diff --git a/oauth/apps.py b/oauth/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5cfc41f3bf3633978a6170c319b016d47c0f549
--- /dev/null
+++ b/oauth/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class OauthConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'oauth'
diff --git a/oauth/migrations/0001_initial.py b/oauth/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d42e8ca246f2f054c8e05107352abeb6220df1e
--- /dev/null
+++ b/oauth/migrations/0001_initial.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.2.13 on 2022-05-01 00:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('auth', '0012_alter_user_first_name_max_length'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='UserAccount',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('password', models.CharField(max_length=128, verbose_name='password')),
+                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+                ('username', models.CharField(max_length=255, unique=True)),
+                ('first_name', models.CharField(max_length=255)),
+                ('last_name', models.CharField(max_length=255)),
+                ('is_active', models.BooleanField(default=True)),
+                ('is_staff', models.BooleanField(default=False)),
+                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
+                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+    ]
diff --git a/oauth/migrations/__init__.py b/oauth/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/oauth/models.py b/oauth/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9ea87be8183abdaba02ad2f168c0efd170ac706
--- /dev/null
+++ b/oauth/models.py
@@ -0,0 +1,45 @@
+from django.db import models
+from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
+
+
+class UserAccountManager(BaseUserManager):
+    def create_user(self, username, password=None, **extra_fields):
+        user = self.model(username=username, **extra_fields)
+
+        user.set_password(password)
+        user.save()
+
+        return user
+
+    def create_superuser(self, username, password, **extra_fields):
+
+        extra_fields.setdefault('is_staff', True)
+        extra_fields.setdefault('is_superuser', True)
+        extra_fields.setdefault('is_active', True)
+
+        if extra_fields.get('is_staff') is not True:
+            raise ValueError(
+                'Superuser must be assigned to is_staff=True.')
+        if extra_fields.get('is_superuser') is not True:
+            raise ValueError(
+                'Superuser must be assigned to is_superuser=True.')
+
+        return self.create_user(username, password, **extra_fields)
+
+class UserAccount(AbstractBaseUser, PermissionsMixin):
+    username = models.CharField(max_length=255, unique=True)
+    first_name = models.CharField(max_length=255)
+    last_name = models.CharField(max_length=255)
+    is_active = models.BooleanField(default=True)
+    is_staff = models.BooleanField(default=False)
+
+    objects = UserAccountManager()
+
+    USERNAME_FIELD = 'username'
+    REQUIRED_FIELDS = ['first_name', 'last_name']
+
+    def get_full_name(self):
+        return "{fname} {lname}".format(fname=self.first_name, lname=self.last_name)
+
+    def __str__(self):
+        return self.username
\ No newline at end of file
diff --git a/oauth/serializers.py b/oauth/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..221d266c11691dbe4055ec647dfc287028c212ab
--- /dev/null
+++ b/oauth/serializers.py
@@ -0,0 +1,22 @@
+from rest_framework import serializers
+from .models import UserAccount
+from django.contrib.auth.password_validation import validate_password
+from rest_framework.exceptions import ValidationError
+
+class UserSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = UserAccount
+        fields = ('username', 'first_name', 'last_name', 'password')
+        extra_kwargs = {'password': {'write_only': True}}
+
+    def create(self, validated_data):
+        password = validated_data.pop('password', None)
+        instance = self.Meta.model(**validated_data)
+        if password is not None:
+            try:
+                validate_password(password=password, user=instance)
+                instance.set_password(password)
+                instance.save()
+                return instance
+            except Exception as err:
+                raise err
diff --git a/oauth/tests.py b/oauth/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6
--- /dev/null
+++ b/oauth/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/oauth/urls.py b/oauth/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..acff786b3531d075a04b6cf7a50a2fbc7d40c8b4
--- /dev/null
+++ b/oauth/urls.py
@@ -0,0 +1,17 @@
+from django.urls import path
+from rest_framework_simplejwt.views import (
+    TokenObtainPairView,
+    TokenRefreshView,
+    TokenVerifyView
+)
+from . import views
+
+app_name = 'oauth'
+
+urlpatterns = [
+    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
+    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
+    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
+    path('api/create-user/', views.UserCreate.as_view()),
+    path('api/resource/', views.ResourceTest.as_view()),
+]
diff --git a/oauth/views.py b/oauth/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b5146c78a4921298e54cae89866eb92490b8c4f
--- /dev/null
+++ b/oauth/views.py
@@ -0,0 +1,34 @@
+from rest_framework import generics
+from rest_framework.exceptions import ValidationError
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from .models import UserAccount
+from .serializers import UserSerializer
+from rest_framework.permissions import AllowAny, IsAuthenticated
+from djangorestframework_camel_case.parser import (CamelCaseJSONParser,
+                                                   CamelCaseMultiPartParser,
+                                                   CamelCaseFormParser)
+from djangorestframework_camel_case.render import (CamelCaseJSONRenderer,
+                                                   CamelCaseBrowsableAPIRenderer)
+
+class UserCreate(generics.CreateAPIView):
+    serializer_class = UserSerializer
+    permission_classes = [AllowAny]
+    parser_classes = (CamelCaseJSONParser, CamelCaseFormParser, CamelCaseMultiPartParser, )
+    renderer_classes = (CamelCaseJSONRenderer, CamelCaseBrowsableAPIRenderer, )
+
+    def perform_create(self, serializer):
+        try:
+            serializer = serializer.save()
+            return serializer
+        except Exception as err:
+            error_message = {'error': err}
+            raise ValidationError(error_message)
+
+# Hanya untuk test token
+class ResourceTest(generics.ListAPIView):
+    queryset = UserAccount.objects.all()
+    serializer_class = UserSerializer
+    permission_classes = [AllowAny]
+    parser_classes = (CamelCaseJSONParser, CamelCaseFormParser, CamelCaseMultiPartParser, )
+    renderer_classes = (CamelCaseJSONRenderer, CamelCaseBrowsableAPIRenderer, )
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 6a4c29b05d0f367509ca0c10a0793e63e9cdb0af..424e42d3e015562619b17876521da882cf6331e2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,9 @@
 coverage>=5.3,<6.0
+django-cors-headers==3.7.0
 dj-database-url>=0.5.0
+djangorestframework==3.11.0
+djangorestframework-camel-case==1.2.0
+djangorestframework-simplejwt>=4.4.0
 Django>=3.1.0,<4.0.0
 gunicorn>=20.0.0,<21.0.0
 psycopg2-binary>=2.8.0,<3.0.0