alpacon
Authentication

Authentication

AIP provides several methods to authenticate users and client machines.

Session authentication

Session authentication is the most basic one, and you can use it to login users on web browsers.

On login, POST should include username, password, and csrf_token. A serialized HTML form with proper fields is accepted.

Login information is set using the session cookies. Please be aware to preserve the cookie and use it for future API access. As Django session authentication requires CSRF protection, you need to include CSRF token if you are using unsafe methods like POST, PUT, PATCH, or DELETE. (Only when using session authentication!)

Following example shows setting CSRF token to HTTP header when using jquery ajax.

import Cookies from "js-cookie";
 
function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
 
$.ajaxSetup({
  beforeSend: function(xhr, settings) {
      if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
          xhr.setRequestHeader("X-CSRFToken", Cookies.get('csrftoken'));
      }
  }
});
 
window.Cookies = Cookies;

References

  1. https://docs.djangoproject.com/en/4.0/ref/csrf/#ajax (opens in a new tab)

APIToken authentication

APIToken is used to authenticate users on applications. Alpacon React is an example.

On successful login, Alpacon Server issues an API token that can be used on further API accesses.

Login

Post username and password to /api/auth/login/.

  • HTTP method: POST
  • Accepted formats: application/json, application/x-www-form-urlencoded, multipart/form-data
  • Request data: username, password
  • Response data: {"token": "xxxxxx", "expires_at": "oooooo"}. Token is a random string with 128 characters.
  • Status codes: 200 OK on success, 403 Forbidden on failure.

The returned token will be valid for 14 days from the last access. Upon the expiry date, any further API access will result in 403 Forbidden.

Using the token

After obtaining the token, include it in the HTTP header on every further request.

  • HTTP header: Authorization: token="xxxxx"

Please make sure to include "" as well.

Check authenticated

Applications can check the authentication status at /api/auth/is_authenticated/.

  • HTTP method: GET
  • Response data: {"authenticated": true or false}
  • Status codes: 200 OK regardless of authentication status.

This interface should always return 200 OK.

Logout

Logout the current user at /api/auth/logout/. Please make sure to include the token in the HTTP header just like other requests.

  • HTTP methods: GET, POST (both works fine.)
  • Request data: null
  • Response data: null
  • Status codes: 200 OK on success, 403 Forbidden on failure. (possibly if not authenticated.)

Token validation

The application should check the status code of every API request to verify the token status. If the token is not valid, Alpacon Server will return 403 Forbidden.

Login sessions

We provide API to list and revoke currently logged in sessions for current user.

Listing login sessions

You can obtain the list of all login sessions for the user.

Request:

GET /api/auth/sessions/

Response:

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": "c0254139-5861-49cc-b5e3-a19e6ceb8c93",
            "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
            "remote_ip": "127.0.0.1",
            "added_at": "2022-11-28T16:57:43.599180+09:00",
            "expires_at": "2022-12-05T16:57:43.599020+09:00"
        },
        {
            "id": "7392bed1-038f-4cfb-9814-0f79e0d90021",
            "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
            "remote_ip": "127.0.0.1",
            "added_at": "2022-11-28T16:56:10.830929+09:00",
            "expires_at": "2022-12-05T16:56:10.830650+09:00"
        }
    ]
}

Revoking login sessions

If you delete a session, the session will be revoked and user will be logged out automatically.

Request:

DELETE /api/auth/sessions/<id>/

API tokens

API tokens can be used to access alpacon by a program.

Creating an API token

You can create an API token with name and expires_at data. Please note that expires_at can be null. In this case, the token will never expire.

Request:

POST /api/auth/tokens/
{
    "name": "test",
    "expires_at": "2022-11-30T00:00:00+09:00"
}

Response:

HTTP 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
    "id": "ff2be1a8-1ae1-4570-9af8-9a21eec4ad22",
    "name": "test",
    "key": "tXaiFo8ltylL877mYhauNBCf2yDsn3kLD2soSeG4PPGF9hfr2cadOyPEofpnDdcW",
    "enabled": true,
    "updated_at": "2022-11-28T18:01:46.966094+09:00",
    "expires_at": null
}

You can use above key as a token for alpacon.

Listing API tokens

You can obtain the list of all API tokens for the user.

Request:

GET /api/auth/tokens/

Response:

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
    "count": 29,
    "next": "http://localhost:8000/api/auth/tokens/?page=2",
    "previous": null,
    "results": [
        {
            "id": "3ce230e4-7cfb-427f-897c-3a98d65482bc",
            "name": "test",
            "enabled": true,
            "updated_at": "2022-11-28T15:54:52.429589+09:00",
            "expires_at": null
        },
        {
            "id": "2c9b43bc-e98b-47e6-b7ec-b34b8c00f48c",
            "name": "hello",
            "enabled": true,
            "updated_at": "2022-11-28T15:53:37.430753+09:00",
            "expires_at": null
        },
        ...
    ]
}

Updating metadata

You can update the name, enabled, and expires_at fields.

Request:

PATCH /api/auth/tokens/3ce230e4-7cfb-427f-897c-3a98d65482bc/
{
    "name": "test",
    "enabled": true,
    "expires_at": null
}

Deleting an API token

You can delete a token if you do not use it anymore.

Request:

DELETE /api/auth/tokens/3ce230e4-7cfb-427f-897c-3a98d65482bc/

APIClient authentication

🚧👷 UNDER CONSTRUCTION

APIClient is used to authenticate servers. Authentication is done through JSON Web Tokens, and the initial login uses the server's unique id and key.

APIClient login

Post ALPAMON_IDand ALPAMON_KEY to /api/apiclient/jwt/login.

  • HTTP method: POST
  • Accepted formats: application/json
  • Request data: id, key
  • Response data: {"access": "xxxxxx", "refresh": "xxxxxx"}. Token is a random string with 128 characters.
  • Status codes: 200 OK on success, 401 Unauthorized on failure.

The access token is valid for 60 minutes after issuance, while the refresh token remains valid for 2 weeks after issuance.

Creating access, refresh token

You can create a token using the id entered through login, expiration time, issued at, and JWT ID.

exp is the expiration time of the token, and iat represents the token issuance time. JWT ID is the unique identifier of this token, and the server can be identified through client_id.

Token iat rule

iat is the token issuance time in token claim.

When issuing new access token with refresh token, exp and Jti are not copied, and iat remains the same as the old token.

The reason for copying iat is to track the time of initial issuance of the access token.

iat of access tokens issued before refresh token expires is the same as the initial issuance time.

Request:

POST /api/apiclient/jwt/login
{
    "id": "ff2be1a8-1ae1-4570-9af8-9a21eec4ad22",
    "key": "xbwikzq"
}

Response:

HTTP 200 OK
Allow: POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
  "token_type": "access",
  "exp": "Fri Oct 06 2023 17:28:10 GMT+0900",
  "iat": "Fri Oct 06 2023 17:27:10 GMT+0900",
  "jti": "3e513a6dec7847c495628614baedc0a7",
  "client_id": "ff2be1a8-1ae1-4570-9af8-9a21eec4ad22"
}
{
  "token_type": "refresh",
  "exp": "Fri Oct 20 2023 17:27:10 GMT+0900",
  "iat": "Fri Oct 06 2023 17:27:10 GMT+0900",
  "jti": "3e513a6dec7847c495628614baedc0a7",
  "client_id": "ff2be1a8-1ae1-4570-9af8-9a21eec4ad22"
}

Using access token

After obtaining the access token for the server, include them in the HTTP header on every further request.

The access token is valid for 60 minutes of authentication to the APIClient.

curl \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNDU2LCJqdGkiOiJmZDJmOWQ1ZTFhN2M0MmU4OTQ5MzVlMzYyYmNhOGJjYSJ9.NHlztMGER7UADHZJlxNG0WSi22a2KaYSfd1S-AuT7lU" \

Token validation

We attempt to validate the access token with every API request to /api/apiclient/auth/APIClientJWTAuthentication. Verify that the signature matches with the Server Secret Key, and then check the token expiration time.

When the access token expires, 403 Forbidden is sent, and you must send a refresh token to reissue it.

The refresh token is validated in /api/apiclient/Serializers/JWTRefreshSerializer.

Verify the refresh token through RefreshToken class, returns a new access token if valid, and returns 401 unauthorized if invalid.

Using refresh token

If the access token has expired, you should request to POST /api/apiclient/jwt/refresh/ with the longer-lived refresh token.

If the refresh token is valid, it generate a new valid access token for client.

But if the refresh token has expired, 401 Unauthorized is generated and alpamon is terminated.

When alpamon terminates, you must restart alpamon again and login.

Request:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"refresh":eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImNvbGRfc3R1ZmYiOiLimIMiLCJleHAiOjIzNDU2NywianRpIjoiZGUxMmY0ZTY3MDY4NDI3ODg5ZjE1YWMyNzcwZGEwNTEifQ.aEoAYkSJjoWH1boshQAaTkf8G3yn0kapko6HFRt7Rh4}' \
  http://localhost:8000/api/apiclient/jwt/refresh/

Response:

  • In case of a valid refresh token request
HTTP 200 OK
Allow: POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
 
{
"access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNTY3LCJqdGkiOiJjNzE4ZTVkNjgzZWQ0NTQyYTU0NWJkM2VmMGI0ZGQ0ZSJ9.ekxRxgb9OKmHkfy-zs1Ro_xs1eMLXiR17dIDBVxeT-w"
}
 
  • In case of an invalid refresh token request

{
    http://localhost:8000 "POST /api/apiclient/jwt/refresh/ HTTP/1.1" 401
    [DEBUG] (alpamon.session) Session termination due to refresh token expiration. 
                              Please restart Alpamon.
}

Password Management

Password change

You can change account passwords by two ways.

Changing password for your own account

You can change your password via /api/auth/change_password/.

  • HTTP method: POST
  • Request data: password, new_password
  • Response data: null
  • Status codes: 200 OK on success, 400 Bad request on failure, 403 Forbidden if unauthenticated.

This interface validates the correctness of current password for security. new_password should satisfy the password security requirements. If one of those checks fails, 400 Bad request will be raised and errors will be set.

After successful change, all open user sessions will be revoked (logged out) automatically. Tokens will be still alive.

Changing passwords for all user accounts (superuser only)

Admin can change the passwords of all user accounts via /api/users/users/<slug:usernmae>/.

  • HTTP method: PUT, PATCH
  • Request data: password
  • Response data: User object
  • Status codes: 200 OK on success, 400 Bad request on failure.

This interface does not validate the correctness of current password, but can only be used by admin users. password should satisfy the password security requirements as /api/auth/change_password/ does.

Password reset

Users can request password reset when they forgot their passwords via /api/auth/reset_password/.

Request:

POST /api/auth/reset_password/
{
    "email": "example@example.com"
}

For successful request, we will send a password reset link to the requested email. Users can set their passwords again through it.

Please note that a user can make 5 pending reset requests at max. If that limit exceeds, we will return 403 Forbidden.

This interface will be rate-limited as it can be exploited by attackers. email should be a registered one. Even though the user puts a wrong email, we will not return 400 Bad request for obfuscation. (By doing so, attackers would not know whether their input is valid.) In this case, the request will be just ignored without any error.

Password reset confirmation

The confirmation link for password reset looks like the followings.

http://localhost:8000/api/auth/reset_password/confirm/WFVzTuCywZfNJQVNoBCgAgvr4FGojkQ9dKnrK0sQiugSiIwpjfONpeSUjueVchLH/

WFVzTuCywZfNJQVNoBCgAgvr4FGojkQ9dKnrK0sQiugSiIwpjfONpeSUjueVchLH is the confirmation key.

Upon successful GET request, API server will redirect the user to alpacon-react. alpacon-react should handle this URL and provide an interface to reset user's password.

If user enters new password, POST it to the server.

POST /api/auth/reset_password/confirm/WFVzTuCywZfNJQVNoBCgAgvr4FGojkQ9dKnrK0sQiugSiIwpjfONpeSUjueVchLH/
{
    "new_password": ""
}

The password will be changed and all pending reset requests for the user will be invalidated. All open sessions for the user will be logged out.