djmongoauth 0.0.1
Out-of-the-box support register, sign in, email verification and password recovery workflows for websites based on Django and MongoDB
License: MIT

# Using djmongoauth

## What is it?
`djmongoauth` provides out-of-the-box support for basic user management and additional operations including user registration, login, logout, email verification, password recovery for backends built with the Django web framework and MongoDB.

`djmongoauth` is based on `djongo`, a MongoDB ORM for Django.

## Use cases
### User object
User object is the core of the `djmongoauth`. It represents a authenticable entity. The primary attributes of a default user instance are:

- `username`
- `email`
- `password`
- `email_verified`
- `email_verified_at`

### Register a new user
```
def register(request):
    req_body = json.loads(request.body.decode("UTF-8"))
    user = User()
    user.username = req_body["username"]
    user.email = req_body["email"]
    user.password = req_body["password"]
    try:
        user.register()
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)
    return HttpResponse(status=201)
```
- `request.method` must be `POST`
- Body of `request` must have these attributes and they must be well-formed: `username`, `email`, `password`. Password can be cleartext (`djmongoauth` takes care of hashing / decryption)

### Log in 
```
def login(request):
    try:
        req_body = json.loads(request.body.decode("UTF-8"))
        x_auth_token = User.login(req_body["username"], req_body["password"])
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)
    return JsonResponse({"token": x_auth_token})
```
- `request.method` must be `POST`
- Body of `request` must have these attributes: `username` and `password`
- `login()` call returns a `x_auth_token`. This token should be returned to your site's frontend and serve as a basic auth token in the `HTTP_AUTHORIZATION` header for all subsequent requests till the token expires

### Log out 
```
def logout(request):
    try:
        User.logout(request)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)
    return HttpResponse(status=204)
```
- `request` must have its `HTTP_AUTHORIATION` header set to the `x_auth_token` returned from `login` call

### Email verification 
```
# handler for verifying email address
def verify_email(request):
    if request.method == "POST":
        return _send_verify_email(request)
    elif request.method == "PUT":
        return _handle_email_verification(request)
    else:
        return HttpResponse(status=405)

def _send_verify_email(request):
    try:
        User.send_email(request, type=EmailTypes.VERIFY)
        return HttpResponse(status=201)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)

def _handle_email_verification(request):
    try:
        User.handle_email_request(request, EmailTypes.VERIFY)
        return HttpResponse(status=200)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)
```

A verification email will be sent to the user's registered email address. Following is a sample verification email:

```
Hello test_user:

Please use the following link to verify your email address on test.com

https://test.com/verify?a=wMw_qmXu8fZOlcHP1Xpku4e8nuo8rCQim0AHzp5Taqtk0CWq2sThbEMu5kVCcy5leVYDpHKfY6-fMc_4HZBbQg

This link will expire on 2021-09-12 02:04:21 UTC

Thank you for using test.com!
```

- `request` must have its `HTTP_AUTHORIATION` header set to the `x_auth_token` returned from `login` call
- To send a verification email, `POST` this endpoint; to handle a email verification request, `PUT` this endpoint with parameter `a` set. Example: `PUT https://api.test.com/verify?a=wMw_qmXu8fZOlcHP1Xpku4e8nuo8rCQim0AHzp5Taqtk0CWq2sThbEMu5kVCcy5leVYDpHKfY6-fMc_4HZBbQg`
- If using a hosted email domain service (example: GSuite), please ensure that options such as *less secure apps* are enabled (Gmail)

### Password reset
```
def reset_password(request):
    if request.method == "POST":
        return _send_recovery_email(request)
    elif request.method == "PUT":
        return _handle_password_recovery(request)
    else:
        return HttpResponse(status=405)

def _send_recovery_email(request):
    try:
        User.send_email(request, type=EmailTypes.RESET)
        return HttpResponse(status=200)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)

def _handle_password_recovery(request):
    try:
        User.handle_email_request(request, EmailTypes.RESET)
        return HttpResponse(status=200)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)
```

A password reset email will be sent to the user's registered email address. Following is a sample password reset email:

```
Hello test_user,

A request has been received to change the password for your account on test.com

Please follow this link to reset your password: https://test.com/reset?a=XfNKZT-OXXvvto3fDAyo5l46Ssmx1wQkXzlYGxQKyhFq3FTNve4vrvNYu8b8ha2erghRWtWfwFT5TT7O9xgM6Q

This link will expire on 2021-09-12 02:34:45 UTC

If you did not initiate this request, please ignore this email.
```

- To send a password reset email, `POST` this endpoint; to handle a password reset request, `PUT` this endpoint with parameter a set. Example: `PUT https://api.test.com/reset?a=wMw_qmXu8fZOlcHP1Xpku4e8nuo8rCQim0AHzp5Taqtk0CWq2sThbEMu5kVCcy5leVYDpHKfY6-fMc_4HZBbQg`
- When `PUT`ting this endpoint, body of `request` must have these attributes: `new_password`. `new_password` can be cleartext (`djmongoauth` takes care of hashing / decryption)

## Decorator
`@authenticated`

Use this decorator on request handlers, etc. to ensure a user is already logged in

```
from djmongoauth.decorators.authenticated import authenticated

@authenticated
def my_other_view_handler(request):
    pass 
```
If a user is not properly authenticated (e.g. not logged in / login session has expired), a `DjMongoAuthError` will be raised