Internet-Draft Key Service Specification HTTP March 2024
Leptons Expires 18 September 2024 [Page]
Workgroup:
Network Working Group
Internet-Draft:
rfce-0002
Published:
Intended Status:
Informational
Expires:
Author:
K. Leptons

Key Service Specification HTTP

Abstract

A simple Application Programming Interface (APIs) for management and authentication key over the Hyper Text Transefer Protocol (HTTP) [RFC2616] or [RFC2660].

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 18 September 2024.

Table of Contents

1. Overview

Unless there is additional specification, the following rules are applied to requests and responses:

Unless there is additional specification, client and server error MUST return the following body:

{
  // A short description for error.
  "message": "foo bar baz"
}

If the example data is too long, then it is trimmed and noted as (more...). For example:

curl ENDPOINT/root/token \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -H 'Authorization: MTZmMpZv-HTgcx_B-4YctpMVAMNa8KxLsx(more...)' \
    -F grant_type=client_credentials

2. Client Resource

Client                                                       Endpoint
  |                                                               |
  |    POST /client/token                                         |
  |==============================================================>|
  |<--------------------------------------------------------------|
  |                                           ClienAccessToken    |
  |                                                               |
  |                                                               |
  |    POST /client/session/token                                 |
  |==============================================================>|
  |<--------------------------------------------------------------|
  |                                         ClientSessionToken    |
  |                                                               |
  |    PUT /client/session                                        |
  |==============================================================>|
  |<--------------------------------------------------------------|
  |                                                         Ok    |
  |                                                               |

2.1. POST /client/token

Request Header

  • Accept: application/json
  • Content-Type: application/json

Request Body

{
  // Type: ApplicationKey
  "application_key": "w_S9E7_8rzehxu_8qeqs7xKLOng=",

  // Type: Username
  "username": "foo",

  // Type: Password
  "password": "bar"
}

Response Body

// 200 OK

{
  // Type: ClientAccessToken
  "access_token": "LwZvJXnrdFP-33iX",

  // Type: Duration
  "expired_in": 3600
}
// 400 - Bad Request

{
  // 400100 - Application key not found.
  // 400101 - Application key disabled.
  // 400102 - Invalid username or password.
  "code": 400100,

  "message": "Invalid Client Key"
}

Response Status

Example

curl ENDPOINT/client/token \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{
        "application_key": "w_S9E7_8rzehxu_8qeqs7xKLOng=",
        "username": "foo",
        "password": "bar"
    }'

2.2. PUT /client/password

Request Header

  • Accept: application/json
  • Content-Type: application/json

Request Body

{
  // Type: Username
  "username": "foo",

  // Type: Password.
  "curent_password": "foo",

  // Type: Password.
  "new_password": "bar"
}

Response Status

  • 204 - The password is updated successfully.
  • 400 - Request is invalid.

Example

curl --request PUT ENDPOINT/client/password \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{
        "username": "foo",
        "current_password": "********",
        "new_password": "********"
    }'

2.3. GET /client/licence

Return the first 8 licences, sort by:

  • created_at Descending.
  • duration Descending.

Request Header

  • Accept: application/json
  • Authorization: Bearer <ClientAccessToken> Section 4.3.

Response Body

[
  {
    // Type: Scope.
    "scope": "mir4_boss",

    // Type: Timestamp.
    "created_at": 1708957712,

    // Type: Timestamp.
    "activated_at": 1708957712,

    // Type: DurationDay
    "duration": 30
  }
]

Response Status

  • 200 - Return list of licences. The result could be empty.
  • 400 - Request is invalid.
  • 401 - Invalid access token.

Example

curl ENDPOINT/client/licence \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json'

2.4. POST /client/session/token

Description

  • If a token is granted for IP address A, then the token is valid for only IP address A.
  • If there are granted tokens for IP address A, and a new token is granted for IP address B, then all tokens for address A become invalid.

Request Header

  • Accept: application/json
  • Authorization: Bearer <ClientAccessToken> Section 4.3.

Request Body

{
  // Type: Scope
  "scope": "mir4booss"
}

Response Body

// 200 - Ok

{
  // Type: ClientSessionToken
  "session_token": "M3p2SU4c0BUEfnmCxE3eYw==",

  // Type: DurationSecond
  "expired_in": 10
}
// 400 - Bad Request

{
  // 400100 - Scope does not exist.
  // 400101 - No licence for scope.
  // 400102 - Licence is not activated yet.
  // 400103 - Licence is expired.
  "code": 400100
}

Example

curl ENDPOINT/client/session/token \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{
        "scope": "mir4boss"
    }'

2.5. PUT /client/session

Request Header

  • Accept: application/json
  • Authorization: Bearer <ClientSessionToken> Section 4.4.

Response Status

  • 204 - The session token is valid and the session will be alive in next ClientAccessTokenLifetime.
  • 401 - The token is invalid. There are reasons: 1 - The token does not exist; 2 - The token expired; 3 - There is a new token is granted for other IP address; 4 - Licence is expired.

Example

curl --request PUT ENDPOINT/client/session \
    -H 'Authorization: Bearer LwZvJXnrdFP-33iX'

3. Root Resource

Client                                                       Endpoint
  |                                                               |
  |    POST /root/token                                           |
  |==============================================================>|
  |<--------------------------------------------------------------|
  |                                            RootAccessToken    |
  |                                                               |
  |                                                               |
  |    POST /root/client                                          |
  |==============================================================>|
  |<--------------------------------------------------------------|
  |                                                        Ok     |
  |                                                               |
  |                                                               |
  |    GET /root/client                                           |
  |==============================================================>|
  |<--------------------------------------------------------------|
  |                                                    Client     |
  |                                                               |
  |                                                               |
  |    POST /root/licence                                         |
  |==============================================================>|
  |<--------------------------------------------------------------|
  |                                                        Ok     |
  |                                                               |
  | ...                                                           |

3.1. POST /root/token

This API complies [RFC6749] (Section 4.4).

Request Header

  • Accept: application/json
  • Content-Type: application/x-www-form-urlencoded
  • Authorization: Basic <RootKey> Section 4.1.

Request Body

grant_type=client_credentials

Response Body

// 200 OK

{
  // Type: RootTokenType
  "token_type": "Bearer",

  // Type: RootAccessToken
  "access_token": "ua3wRxUr7Sq495Ai6DofioRZd55ZbLJa_D0ZbCJM86U=",

  // Type: Timestamp
  "expires_in": 3600
}
// 400 Bad Request

{
  // See RFC6749, section 5.2 for list of values.
  "error": "invalid_request"
}

Response Status

  • 200 - A new token is generated.
  • 400 - Request data format is invalid. Or credential is invalid.

Example

curl https://key.com/root/token \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -H 'Authorization: MTZmMpZv-HTgcx_B-4YctpPVAMNa8KxLsx(more...)' \
    -F grant_type=client_credentials

3.2. POST /root/client

Request Header

  • Accept: application/json
  • Content-Type: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Body

{
  // Type: Username
  "username": "foo",

  // Type: Password
  "password": "********",

  // Type: Email
  // Required: Yes if `phone_number` and `zalo_id` are empty.
  "email": "foo@mail.com",

  // Type: PhoneNumber
  // Required: Yes if `email` and `zalo_id` are empty.
  "phone_number": "091 111 1234",

  // Type: ZaloIdentity
  // Required: Yes if `email` and `phone_number` are empty.
  "zalo_id": "foo-xyz"
}

Response Body

{
  // Type: ClientIdentity
  "id": "I0Cy6wd2bj_k0J7idEmnPw=="
}

Response Status

  • 201 - A new client is created.
  • 400 - Request is invalid.
  • 409 - The field username is already exists.
  • 401 - The access token is invalid.

Example

curl ENDPOINT/root/client \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU=' \
    -d '{
        "username": "foo",
        "password": "********",
        "email": "foo@mail.com",
        "phone_number": null,
        "zalo_id": null
    }'

3.3. GET /root/client

Request Header

  • Accept: application/json
  • Content-Type: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Query

  • q: Search keyword. Could be username, email, phone number or Zalo ID.

Response Body

[
  {
    // Type: ClientIdentity
    "id": "dVUrGHvBd9ni163yau99NA==",

    // Type: Username
    "username": "foo"
  },
  {
    "id": "SlU7uk6HKmUVuz4kEu1ckg==",
    "username": "bar"
  },
  {
    "id": "dxNfBeRZvOwavg_Kuri5sw==",
    "username": "baz"
  }
]

Response Status

  • 200 - Searching successfully. The result could be empty.
  • 400 - Request is invalid.
  • 401 - The access token is invalid.

Example

curl ENDPOINT/root/client \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU=' \
    -G -d q=foo

3.4. GET /root/client/:id

Request Header

  • Accept: application/json
  • Content-Type: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Parameter

Response Body

{
  // Type: Username
  "username": "foo",

  // Type: Email
  // Required: Yes if `phone_number` and `zalo_id` are empty.
  "email": "foo@mail.com",

  // Type: PhoneNumber
  // Required: Yes if `email` and `zalo_id` are empty.
  "phone_number": "091 111 1234",

  // Type: ZaloIdentity
  // Required: Yes if `email` and `phone_number` are empty.
  "zalo_id": "foo-xyz",

  // Type: Timestamp
  "created_at": 1709113736,

  // Type: Timestamp
  "upated_at": 1709113736,

  // Type: Timestamp
  "accessed_at": 1709113736
}

Response Status

  • 200 - The client is found.
  • 400 - Request is invalid.
  • 401 - The access token is invalid.
  • 404 - The client does not exist.

Example

curl ENDPOINT/root/client/dxNfBeRZvOwavg_Kuri5sw== \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU='

3.5. PATCH /root/client/:id

Request Parameter

Request Header

  • Accept: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Body

{
  // Type: Password
  // Requied: No
  "password": "********",

  // Type: Email
  // Required: Yes if `phone_number` and `zalo_id` are empty.
  "email": "foo@mail.com",

  // Type: PhoneNumber
  // Required: Yes if `email` and `zalo_id` are empty.
  "phone_number": "091 111 1234",

  // Type: ZaloIdentity
  // Required: Yes if `email` and `phone_number` are empty.
  "zalo_id": "foo-xyz"
}

Response Status

  • 204 - The client is updated successfully.
  • 400 - Request is invalid.
  • 401 - The access token is invalid.
  • 404 - The client does not exist.

Example

curl ENDPOINT/root/client/I0Cy6wd2bj_k0J7idEmnPw== \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU=' \
    -d '{
        "password": "********",
        "email": "foo@mail.com",
        "phone_number": null,
        "zalo_id": null
    }'

3.6. POST /root/licence

Request Header

  • Accept: application/json
  • Content-Type: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Body

{
  // Type: ClientIdentity
  "client_id": "dxNfBeRZvOwavg_Kuri5sw==",

  // Type: Scope
  "scope": "mir4_boss",

  // Type: LicenceDuration
  "duration": 30
}

Response Body

{
  // Type: LicenceIdentity
  "id": "HHLj5_UmdI83B3GnnWzuKg=="
}

Response Status

  • 201 - The licence is created.
  • 400 - Request is invalid.
  • 401 - The access token is invalid.

Example

curl ENDPOINT/root/licence \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU=' \
    -d '{
        "client_id": "dxNfBeRZvOwavg_Kuri5sw==",
        "scope": "mir4_boss",
        "duration": 30
    }'

3.7. GET /root/licence/:id

Request Header

  • Accept: application/json
  • Content-Type: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Parameter

Response Body

{
  // Type: ClientIdentity
  "client_id": "dxNfBeRZvOwavg_Kuri5sw==",

  // Type: Username
  "end_user_username": "foo",

  // Type: Scope
  "scope": "mir4_boss",

  // Type: LicenceDuration
  "duration": 30,

  // Type: Timestamp
  "activated_at": 1709113736,

  // Type: Timestamp
  "created_at": 1709113736,

  // Type: Timestamp
  "accessed_at": 1709113736
}

Response Status

  • 200 - The licence is found.
  • 400 - Request is invalid.
  • 401 - The access token is invalid.
  • 404 - The licence does not exist.

Example

curl ENDPOINT/root/licence/_ZFr2c4lkKoohtNc_lNpmQ== \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU='

3.8. PATCH /root/licence/:id

Request Header

  • Accept: application/json
  • Content-Type: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Parameter

Response Body

{
  // Type: Scope
  "scope": "mir4_boss",

  // Type: LicenceDuration
  "duration": 30
}

Response Status

  • 204 - The licence is updated successfully.
  • 400 - Request is invalid.
  • 401 - The access token is invalid.
  • 404 - The licence does not exist.

Example

curl ENDPOINT/root/licence/_ZFr2c4lkKoohtNc_lNpmQ== \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU=' \
    -d '{
        "scope": "mir4_boss",
        "duration": 30
    }'

3.9. GET /root/licence/:client_id

Request Header

  • Accept: application/json
  • Content-Type: application/json
  • Authorization: Bearer <RootAccessToken> Section 4.5.

Request Parameter

Response Body

[
  {
    "id": "ylw8UJ4lEiVKPDYQtWQCpA==",
    "scope": "mir4_boss",
    "duration": 30
  },
  {
    "id": "ic65RO1hFkfUXHQuUz50dg==",
    "scope": "mir4_boss",
    "duration": 30
  },
  {
    "id": "8dgXmOA6gCgTAyV58m1Xmw==",
    "scope": "mir4_bosss",
    "duration": 30
  }
]

Response Status

  • 200 - Searching successfully. The result could be empty.
  • 400 - Request is invalid.
  • 401 - The access token is invalid.
  • 404 - The client does not exist.

Example

curl ENDPOINT/root/licence/Oeo5jMbjEVh8AP0O8rm2Jw== \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: 1UOs7lWu4p1iKcBGptACYOVNcw-ZVzIRVOybQbJgGJU='

4. Type

4.1. RootKey

  • Binary size: 256 bits.
  • Encoded: Base64Url.

It is used for authentication from client to sever as a root user, see Section 3.

A key MUST be unique, and generated by a strong cryptography algorithm such as RAND_bytes(3) from OpenSSL.

4.2. ApplicationKey

  • Binary size: 160 bits.
  • Encoded: Base64Url.

It reprents for the client that has permission to using APIs. Without the key, other type of keys cannot be authenticated.

A key MUST be unique, and generated by a strong cryptography algorithm such as RAND_bytes(3) from OpenSSL.

Using with RootKey for authentication as a root user. See Section 3.1.

4.3. ClientAccessToken

  • Binary size: 96 bits.
  • Encoded: Base64Url.

A credential to access client resource.

4.4. ClientSessionToken

  • Binary size: 96 bits.
  • Encoded: Base64Url.

A credential to keep session alive.

4.5. RootAccessToken

  • Binary size: 256 bits.
  • Encoded: Base64Url.

A credential to access root resource.

A key MUST be unique, and generated by a strong cryptography algorithm such as RAND_bytes(3) from OpenSSL.

A description about the key, can be user name, phone number or address.

4.6. Timestamp

UNIX timestamp in seconds.

4.7. Duration

A duration in seconds.

4.8. ClientIdentity

  • Binary size: 128 bits.
  • Encoded: Base64Url.
  • Generator: UUID v4.

4.9. LicenceIdentity

  • Binary size: 128 bits.
  • Encoded: Base64Url.
  • Generator: UUID v4.

4.10. Scope

Represent for a tool. One of the following values:

  • mir4_boss.

4.11. Username

  • Type: string.
  • Length: 1-16.
  • Encoding: UTF-8.

4.12. Password

  • Type: string.
  • Length: 8-32.
  • Encoding: UTF-8.

5. Endpoint Configuration

5.1. ClientAccessTokenLifetime

A time duration in seconds. If a ClientAccessToken is not sent by PUT /client/session during this time, then it will be expired.

5.2. RootAccessTokenLifetime

A time duration in seconds. A RootAccessToken will be expired after this duration.

6. Informative References

[RFC2616]
Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, DOI 10.17487/RFC2616, , <https://www.rfc-editor.org/info/rfc2616>.
[RFC2660]
Rescorla, E. and A. Schiffman, "The Secure HyperText Transfer Protocol", RFC 2660, DOI 10.17487/RFC2660, , <https://www.rfc-editor.org/info/rfc2660>.
[RFC4648]
Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, , <https://www.rfc-editor.org/info/rfc4648>.
[RFC6749]
Hardt, D., Ed., "The OAuth 2.0 Authorization Framework", RFC 6749, DOI 10.17487/RFC6749, , <https://www.rfc-editor.org/info/rfc6749>.
[RFC8259]
Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, , <https://www.rfc-editor.org/info/rfc8259>.

Author's Address

Kevin Leptons