| Internet-Draft | Key Service Specification HTTP | March 2024 |
| Leptons | Expires 18 September 2024 | [Page] |
A simple Application Programming Interface (APIs) for management and authentication key over the Hyper Text Transefer Protocol (HTTP) [RFC2616] or [RFC2660].¶
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.¶
Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Unless there is additional specification, the following rules are applied to requests and responses:¶
// foo bar baz.¶ Content-Type MUST be application/json.¶ 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
¶ Client Endpoint | | | POST /client/token | |==============================================================>| |<--------------------------------------------------------------| | ClienAccessToken | | | | | | POST /client/session/token | |==============================================================>| |<--------------------------------------------------------------| | ClientSessionToken | | | | PUT /client/session | |==============================================================>| |<--------------------------------------------------------------| | Ok | | |¶
Request Header¶
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¶
200 - A new token is generated. All previous tokens become invalid. If there is no request PUT /client/session, then the token will be expired after ClientAccessTokenLifetime.¶ 400 - The keys are invalid.¶ 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"
}'
¶ Request Header¶
Request Body¶
{
// Type: Username
"username": "foo",
// Type: Password.
"curent_password": "foo",
// Type: Password.
"new_password": "bar"
}
¶ Response Status¶
Example¶
curl --request PUT ENDPOINT/client/password \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"username": "foo",
"current_password": "********",
"new_password": "********"
}'
¶ Return the first 8 licences, sort by:¶
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'
¶ Description¶
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"
}'
¶ 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'
¶ Client Endpoint | | | POST /root/token | |==============================================================>| |<--------------------------------------------------------------| | RootAccessToken | | | | | | POST /root/client | |==============================================================>| |<--------------------------------------------------------------| | Ok | | | | | | GET /root/client | |==============================================================>| |<--------------------------------------------------------------| | Client | | | | | | POST /root/licence | |==============================================================>| |<--------------------------------------------------------------| | Ok | | | | ... |¶
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¶
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
¶ 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
}'
¶ 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
¶ Request Header¶
Accept: application/json¶ Content-Type: application/json¶ Authorization: Bearer <RootAccessToken> Section 4.5.¶ Request Parameter¶
:id Section 4.8.¶ 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='
¶ Request Parameter¶
:id Section 4.8.¶ 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
}'
¶ 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¶
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
}'
¶ Request Header¶
Accept: application/json¶ Content-Type: application/json¶ Authorization: Bearer <RootAccessToken> Section 4.5.¶ Request Parameter¶
:id Section 4.9¶ 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='
¶ Request Header¶
Accept: application/json¶ Content-Type: application/json¶ Authorization: Bearer <RootAccessToken> Section 4.5.¶ Request Parameter¶
:id Section 4.9¶ 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
}'
¶ Request Header¶
Accept: application/json¶ Content-Type: application/json¶ Authorization: Bearer <RootAccessToken> Section 4.5.¶ Request Parameter¶
client_id Section 4.8¶ 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='
¶ 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.¶
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.¶
A credential to access client resource.¶
A credential to keep session alive.¶
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.¶
A time duration in seconds. If a ClientAccessToken is not sent by PUT /client/session during this time, then it will be expired.¶
A time duration in seconds. A RootAccessToken will be expired after this duration.¶