OAuth.name Development Documentation

← Back

...Work in Progress...

OAuth.name enables developers to seamlessly integrate Telegram SSO into their web applications, allowing users to sign in using their Telegram accounts. This eliminates the need for users to create and manage new usernames and passwords, streamlining the login process.

OAuth.name offers two distinct login flows:

Simple Login Flow

Simple Login Flow

This lightweight flow focuses on authentication and directly provides the user's Telegram ID and access token to the web application. However, access management is handled entirely on the client-side.

  1. Create an OAuth.name Client: Register your web application with OAuth.name to obtain a application ID and secret key.

  2. Implement Authorization Access Token Grant: Redirect users to OAuth.name for authentication, receiving an access token upon successful login.

  3. Store Access Token and User Information: Securely store the access token and user information (username, telegram_id) for future use.

  4. Verify Access Token on Subsequent Requests: Include the access token in subsequent requests to OAuth.name's API to verify user authorization.

Extended Login Flow

Extended Login Flow

This robust flow introduces an access token endpoint, enabling the web application to manage and refresh access tokens securely on the OAuth.name side.

  1. Create an OAuth.name Client: Register your web application with OAuth.name to obtain a application ID and secret key.

  2. Implement Authorization Access Token Grant: Redirect users to OAuth.name for authentication, receiving an access token upon successful login.

  3. Store Access Token and User Information: Securely store the access token and user information (username, telegram_id) for future use.

  4. Verify Access Token on Subsequent Requests: Include the access token in subsequent requests to OAuth.name's API to verify user authorization.

  5. Implement Refresh Token Mechanism: Implement a token refresh mechanism to obtain a new access token when the current one expires.

  6. Handle Expired Access Token: Upon receiving an expired access token, redirect the user to OAuth.name for reauthentication and obtain a new access token.


Token structure

OAuth.name access tokens are JSON Web Tokens (JWTs) that encode user information and authorization details. The structure of the token ensures secure and verifiable transfer of user data between OAuth.name's authorization server and your web application.

The token structure includes the following fields:

  • username: The unique username associated with the user's Telegram account.

  • telegram_id: The unique identifier of the user's Telegram account.

  • first_name (optional): The user's first name, if available from Telegram.

  • last_name (optional): The user's last name, if available from Telegram.

  • photo_url (optional): The URL of the user's profile picture, if available from Telegram.

  • created_at: The Unix timestamp indicating when the token was issued.

  • permission (optional): The specific permission granted to the user during authentication or token refresh.


{

  "username": "superman",

  "telegram_id": 12034340,

  "first_name": "John",

  "last_name": "Silver",

  "photo_url": "https://t.me/photo_url",

  "created_at": 1703601386,

  "permission": "admin",

}

The token structure is designed to provide a standardized and secure way to exchange user information and authorization data between OAuth.name and your web application. This ensures that user data remains protected and accessible only to authorized applications.

Token Expiration Management

In the OAuth.name platform, token expiration is not explicitly managed or enforced. Instead, each token contains a created_at parameter that indicates the date and time when it was issued. It's up to developers to handle token expiration based on their specific application requirements.

By including the created_at parameter in the token payload, developers have the flexibility to implement their own logic to determine when a token should be considered expired. You can compare the created_at timestamp with the current time and define your own expiration threshold or duration.

Here are some common approaches developers can take to handle token expiration:

  • Time-based expiration: You can set a predefined expiration time for tokens, such as a specific duration (e.g., 1 hour, 24 hours) from the created_at timestamp. After this duration has passed, the token would be considered expired, and users would need to re-authenticate.

  • Sliding expiration: With this approach, the expiration time can be extended with each interaction or activity from the user. For example, if the user performs an action within a certain timeframe before the token's expiration, the expiration time is extended. This approach allows for more seamless user experiences without requiring frequent re-authentication.

Remember to consider the sensitivity and security requirements of your application when implementing token expiration. It's crucial to strike a balance between convenience and security to ensure that tokens remain valid for an appropriate duration without compromising user accounts or data.

By providing developers the flexibility to handle token expiration, OAuth.name allows you to tailor the expiration logic to best suit your application's specific needs. This empowers you to implement token expiration mechanisms that align with your desired user experience and security considerations.

Token Encryption and Decryption

OAuth.name utilizes AES-256-CBC encryption to protect the confidentiality and integrity of access tokens. This section provides detailed documentation on the encryption and decryption mechanisms used in the OAuth.name service.

Encryption

The encryptToken function securely encrypts a token using the following steps:

  1. Generate a Private Key: A private key is derived from the combination of the application ID and secret key, ensuring that only authorized parties can generate valid tokens.

  2. Create a Random Initialization Vector (IV): An IV serves as a unique starting point for the encryption process, ensuring that the same plaintext cannot be encrypted to the same ciphertext multiple times.

  3. Perform AES-256-CBC Encryption: The plaintext is encrypted using the AES-256-CBC algorithm with the generated key and IV.

  4. Generate a HMAC Hash: A cryptographic hash is generated using the SHA-256 algorithm to protect against tampering with the encrypted data.

  5. Concatenate IV, Hash, and Ciphertext: The generated IV, hash, and ciphertext are concatenated into a single string.

  6. Encode the Concatenated Data: The concatenated string is base64-encoded to ensure compatibility with various data formats and transfer protocols.

Decryption

The decryptToken function securely decrypts a token using the following steps:

  1. Decode the Base64-Encoded Data: The base64-encoded string is decoded to obtain the original IV, hash, and ciphertext.

  2. Extract IV, Hash, and Ciphertext: The IV, hash, and ciphertext are individually extracted from the decoded string.

  3. Generate the Private Key: The private key is derived from the combination of the application ID and secret key.

  4. Verify the Hash: The HMAC hash is recalculated using the generated key and the concatenation of the ciphertext and IV.

  5. Perform AES-256-CBC Decryption: The ciphertext is decrypted using the AES-256-CBC algorithm with the generated key and IV.

  6. Return the Decrypted Text: The decrypted text is returned as the plaintext representation of the token.

Code examples

TypeScript


import { Cipher } from 'crypto';

import { createHash } from 'crypto';



const encryptToken = (plaintext: string, password: string): string => {

  const key = createHash('sha256').update(password).digest('hex');

  const iv = generateRandomBytes(16);

  const ciphertext = new Cipher('AES-256-CBC', { iv: iv.toString('base64'), key: key.toString('base64') }).encrypt(plaintext);

  const hash = createHash('sha256').update(ciphertext.toString('base64') + iv.toString('base64')).digest('hex');

  return iv.toString('base64') + hash + ciphertext.toString('base64');

};



const decryptToken = (ivHashCiphertextSource: string, password: string): string | null => {

  const ivHashCiphertext = base64Decode(ivHashCiphertextSource);

  const iv = base64Decode(ivHashCiphertext.substring(0, 16));

  const hash = base64Decode(ivHashCiphertext.substring(16, 48));

  const ciphertext = base64Decode(ivHashCiphertext.substring(48));

  const key = createHash('sha256').update(password).digest('hex');



  if (!crypto.createHash('sha256').update(ciphertext + iv).digest('hex') === hash) return null;



  return new Cipher('AES-256-CBC', { iv: iv.toString('base64'), key: key.toString('base64') }).decrypt(ciphertext).toString();

};

Python


from Crypto.Cipher import AES

from Crypto.Random import get_random_bytes

import hashlib

import base64

import hmac



def encrypt_token(plaintext, password):

    method = "AES-256-CBC"

    key = hashlib.sha256(password.encode()).digest()

    iv = get_random_bytes(16)



    cipher = AES.new(key, AES.MODE_CBC, iv)

    ciphertext = cipher.encrypt(plaintext.encode())

    hash_value = hmac.new(key, ciphertext + iv, hashlib.sha256).digest()



    return base64.b64encode(iv + hash_value + ciphertext).decode()



def decrypt_token(iv_hash_ciphertext_source, password):

    iv_hash_ciphertext = base64.b64decode(iv_hash_ciphertext_source)

    method = "AES-256-CBC"

    iv = iv_hash_ciphertext[:16]

    hash_value = iv_hash_ciphertext[16:48]

    ciphertext = iv_hash_ciphertext[48:]

    key = hashlib.sha256(password.encode()).digest()



    if not hmac.compare_digest(hashlib.sha256(ciphertext + iv).digest(), hash_value):

        return None



    cipher = AES.new(key, AES.MODE_CBC, iv)

    decrypted_text = cipher.decrypt(ciphertext).decode('utf-8')

    return decrypted_text

Golang


package main



import (

  "crypto/aes"

  "crypto/cipher"

  "crypto/rand"

  "crypto/sha256"

  "encoding/base64"

  "hash"

  "io"

)



func encryptToken(plaintext, password string) (string, error) {

  method := "AES-256-CBC"

  key := sha256.Sum256([]byte(password))

  iv := make([]byte, 16)

  if _, err := io.ReadFull(rand.Reader, iv); err != nil {

    return "", err

  }



  block, err := aes.NewCipher(key[:])

  if err != nil {

    return "", err

  }



  ciphertext := make([]byte, len(plaintext))

  mode := cipher.NewCBCEncrypter(block, iv)

  mode.CryptBlocks(ciphertext, []byte(plaintext))



  hasher := hmac.New(func() hash.Hash { return sha256.New() }, key[:])

  hasher.Write(ciphertext)

  hasher.Write(iv)

  hashValue := hasher.Sum(nil)



  encryptedData := append(iv, hashValue...)

  encryptedData = append(encryptedData, ciphertext...)



  return base64.StdEncoding.EncodeToString(encryptedData), nil

}



func decryptToken(ivHashCiphertextSource, password string) (string, error) {

  ivHashCiphertext, err := base64.StdEncoding.DecodeString(ivHashCiphertextSource)

  if err != nil {

    return "", err

  }



  method := "AES-256-CBC"

  key := sha256.Sum256([]byte(password))

  iv := ivHashCiphertext[:16]

  hashValue := ivHashCiphertext[16:48]

  ciphertext := ivHashCiphertext[48:]



  hasher := hmac.New(func() hash.Hash { return sha256.New() }, key[:])

  hasher.Write(ciphertext)

  hasher.Write(iv)

  expectedHash := hasher.Sum(nil)



  if !hmac.Equal(hashValue, expectedHash) {

    return "", nil

  }



  block, err := aes.NewCipher(key[:])

  if err != nil {

    return "", err

  }



  mode := cipher.NewCBCDecrypter(block, iv)

  decryptedData := make([]byte, len(ciphertext))

  mode.CryptBlocks(decryptedData, ciphertext)



  return string(decryptedData), nil

}

PHP


function encryptToken($plaintext, $password) {

    $method = "AES-256-CBC";

    $key = hash('sha256', $password, true);

    $iv = openssl_random_pseudo_bytes(16);



    $ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);

    $hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);



    return base64_encode($iv . $hash . $ciphertext);

}



function decryptToken($ivHashCiphertextSource, $password) {

    $ivHashCiphertext = base64_decode($ivHashCiphertextSource);

    $method = "AES-256-CBC";

    $iv = substr($ivHashCiphertext, 0, 16);

    $hash = substr($ivHashCiphertext, 16, 32);

    $ciphertext = substr($ivHashCiphertext, 48);

    $key = hash('sha256', $password, true);



    if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;



    return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);

}
Security Considerations

The AES-256-CBC encryption algorithm provides strong cryptographic protection against unauthorized decryption. However, it's essential to handle the secret key securely, such as storing it in a secure environment and not transmitting it over insecure channels. Additionally, it's crucial to verify the hash to ensure the integrity of the token and prevent tampering.

Token refresh

The token refresh process is used to obtain a new access token when the current access token expires. This process is done to maintain the user's session without the need for them to re-authenticate.

To refresh a token, the application should send a POST request to the https://sso.oauth.name/refresh endpoint with refresh_token and application_id.

Request body example


{

  "refresh_token": "0ye33t790as09d9asd988-34s=",

  "application_id": "647900494d86d"

}

The response will contain a new access token.

Login redirect

The login redirect is used to redirect the user to the OAuth.name service to authenticate. The application should redirect the user to the following URL:

https://sso.oauth.name/access/?grant={application_id}

Where application_id is identifier of your service.

User can login only when redirected from host domain or its subdomains.

Authorized user redirect

The authorized user redirect is used to redirect the user back to your application after they have authenticated. The redirect URL can be changed in application settings.

Glossary

Application ID

A unique identifier for an application that is registered with a OAuth.name service. It is typically used to authenticate the application and to access the OAuth.name API.

Host domain

The domain name of the server that hosts an application. It is often used in URLs to identify the application and to direct traffic to the correct server.

Private key

A combination of the application ID and secret key that is used to sign requests to a OAuth.name service. The private key is typically stored securely and is not shared with anyone other than the application owner.

{application_id}:{secret_key} – replace {application_id} and {secret_key} with corresponding values.

Redirect URL

A URL that is specified by an application when it redirects the user to the OAuth.name service. The redirect URL is used by the service to return the user to the application after they have completed the authentication process.

Refresh token

A long-lived token that is generated and stored by the application itself. The refresh token is encrypted with the authorized user's Telegram ID, and it can be used to obtain a new access token when the current access token expires. This process is done to maintain the user's session without the need for them to re-authenticate.

Secret key

A secret key that is used to protect an application's data. The secret key is typically stored securely and is not shared with anyone other than the application owner.

Token

A string of characters that is used to identify a user. Tokens are typically issued by OAuth.name services to authenticate users.


Home
Applications
FAQ
Documentation
- Administration
- Development
- Glossary
Terms of service
Privacy policy
Cookie policy
 


© 2024 OAuth.name · Made with ♥
Applications · FAQ · Documentation · Privacy · X (Twitter)