Rapidly build your own stateless REST-API with neoan3
Tags: neoan3, php, jwt, authentication
Published: 2021-10-19 01:47:16

***Coding along should approx. take you 15 min***

What we will be working on:

- Setup
- Adding a simple model
- Writing authentication endpoints
- Final thoughts

## Setup

Since we don't want to spend a lot of time on addressing various environments, let's check out the official docker setup for neoan3:

`git clone https://github.com/neoan3/docker.git`

Our only constraint is the need for docker-compose, so be sure you have that installed (comes with Docker Desktop on Mac & Windows). In order to get started, we will run the following commands:

`docker-compose up -d --build`

`docker-compose exec neoan3 sh`

That's it! The hashtag you will see indicates you are executing the service's shell as root.

From here, let's finally get started and create a neoan3 project:

`neoan3 new app`

To verify everything works as expected, try visiting http://localhost:8090

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1f2z10bz4o3qm3jtamuc.png)

## Adding a simple user model

The easiest way to get fast results is to use the basic email login. 

`neoan3 add model https://github.com/neoan3/email-password-auth.git`

Later, it makes sense to look at the few lines of relevant code in the folder _model/user_ , but for now we will jump over to migrating the model with our database that shipped with our docker setup. To do so, all you need it to run 

`neoan3 migrate models up` and then choose ***neoan3_db*** as the credential key.

To verify the installation of our model has worked as expected, visit http://localhost:8090/migrate and see if you can find "user" in the dropdown.

_NOTE:_ Don't worry about the security-warning you will see on that page. This is just a reminder that you shouldn't deploy this setup without taking the steps lined out in the README of the docker repo we started out with.
## Writing authentication endpoints

Let's finally code something, right? Well, almost. Let's get us started with some boilerplate for our endpoints:

`neoan3 new component auth -t:api -f:demo`

This command will get us to a good starting point and within your project you should find a folder [app]/component/Auth with a file AuthController.php. And that's the file we will work with.

### The plan

We want to have the following endpoints at our disposal

- POST /auth (this is our login-endpoint)
- POST /auth/register (this is, well, you guessed right)
- GET /auth (this returns a logged in user or a 401 unauthorized response. We mainly want that for testing.)

FYI: the default api-endpoint behavior is using neoan3 api v1. Our endpoints will therefore be at http://localhost:8090/api.v1/auth### Let's first look at the method postAuth

Since we want both /auth and /auth/register to be handles by this method, our postAuth should look like this:

```php
function postAuth(string $mode = 'Login', array $body=[]): array
{
    if($mode === 'Login'){
        // for endpoint /auth (or /auth/login)
    }
    if($mode === 'Register'){
        // for endpoint /auth/register
    }
    return [];
}
```
As you can see, the API converts kebab-case to PascalCase, so something like /auth/log-me-in would be interpreted as LogMeIn. 

### adding model transactions

We are using PHP8 in our docker setup. This makes it possible to load injections via Attribute
My IDE does most of this for me, but be sure to have both
`use Neoan3\Provider\Model\InitModel;` and
`use Neoan3\Model\User\UserModel;` after the namespace of your file


```php
// 1. Load our user model
#[InitModel(UserModel::class)]
function postAuth(string $mode = 'Login', array $body=[]): array
{
    // 2. pass the payload (body) to the corresponding model-method
    if($mode === 'Login'){
        // for endpoint /auth (or /auth/login): send the credentials to login
        $user = UserModel::login($body);
    }
    if($mode === 'Register'){
        // for endpoint /auth/register: send the credentials to register
        $user = UserModel::register($body);
    }
    // 3. create a JWT token
    $auth = $this->provider['auth']->assign($user['id'], ['user','self'], $user);
    return ['token' => $auth->getToken()];
}
```
### GET /auth

In order to try this out without a front-end we can use neoan3's on-board helper located at http://localhost:8090/endpoint

But first, let's built our GET-method. The most basic usage would be to restrict access to our GET-endpoint unless authenticated:

```php

// 1. Add the authorization Attribute
#[Authorization('restrict')]
function getAuth(): array
{
    // 2. read the JWT
    $auth = $this->provider['auth']->validate();
    // 3. return its content to the client without exposing the identifier
    return $auth->getPayload();
}
``` 
Now we can jump over to http://localhost:8090/endpoint and start with our test-scenario:

1. Creating a user:

Set  the endpoint to "auth/register" with the method POST, adding a JSON-payload like 
`{"email":"test@example.com","password":"secure-me"}`

When hitting send you should get an answer with a token

2. Test authentication

Copy the content of the token and paste it into the input field at the authorization-tab, then switch the method to GET after setting the endpoint to auth.

You should now receive your user back.
![endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ljjt6htte6va5thklqt1.png)

## Final thoughts

First of all: did you run in any kind of issues? Let me know. Otherwise, I hope you are able to derive how easy it is to work with persistent data with neoan3 beyond a user model. Lastly, let's talk about CORS.
I don't know what your plans for the frontend are. Whenever you get into a scenario where you run your application outside of the docker container (e.g. while developing a VueJS app), you will likely have a development server with a port (e.g. 8080). In order to work with cross-origin requests, please find the file ***default.php*** and adjust the allowed_origins accordingly (wildcard works, but be aware of the implications)

Happy coding