Token-Based Authentication with Multiple Login Methods in Mini-Shop-Server

The mini-shop-server implements a unified token-based authentication layer that supports five distinct login mechanisms—username, email, mobile, and three WeChat variants—through a centralized dispatch service that generates itsdangerous signed tokens for stateless API security.

The allen7d/mini-shop-server repository demonstrates a Flask-based approach to handling diverse authentication methods under a single architecture. Rather than implementing separate authentication flows for each login type, the system uses a strategy pattern to route credentials through specialized verifiers while issuing standardized tokens for session management.

Authentication Architecture Overview

The implementation splits responsibilities across three core components. The API entry point in app/api/v1/token.py receives incoming credentials, the login verification service in app/service/login_verify.py handles method-specific validation, and the token utilities in app/core/token_auth.py manage cryptographic signing and validation.

This separation allows the system to treat a WeChat Mini-Program code and a traditional password with equal abstraction—both resolve to a user identity and a signed token containing the user ID, login type, and permission scope.

Login Method Dispatch Strategy

The LoginVerifyService.get_token method acts as a router, mapping each ClientTypeEnum value defined in app/libs/enums.py to a specific verification handler. This dispatch pattern enables the single /v1/token endpoint to handle all authentication variants without conditional branching in the view layer.

Internal Credential Methods

For traditional logins, the system supports three identifier types stored in the Identity model:

  • Username (ClientTypeEnum.USERNAME, value 100)
  • Email (ClientTypeEnum.EMAIL, value 101)
  • Mobile (ClientTypeEnum.MOBILE, value 102)

Each method follows an identical verification pattern defined in app/service/login_verify.py. The verify_by_username, verify_by_email, and verify_by_mobile functions query the Identity table, validate the provided password against a SHA-256 hash stored in the _credential field, and retrieve the associated User record.


# app/service/login_verify.py (conceptual dispatch)

promise = {
    ClientTypeEnum.USERNAME: LoginVerifyService.verify_by_username,
    ClientTypeEnum.EMAIL:    LoginVerifyService.verify_by_email,
    ClientTypeEnum.MOBILE:   LoginVerifyService.verify_by_mobile,
}
identity = promise[ClientTypeEnum(type)](account, secret)

WeChat OAuth Integration

The server integrates three distinct WeChat login flows, each exchanging a temporary code for a persistent user identifier:

  • WeChat Mini-Program (ClientTypeEnum.WX_MINA, value 200): Uses verify_by_wx_mina to exchange the code for an OpenID via app/service/wx_token.py, creating or fetching the user via UserDao.register_by_wx_mina.
  • WeChat Open Platform (ClientTypeEnum.WX_OPEN, value 202): Handles web QR logins through verify_by_wx_open, calling app/service/open_token.py to resolve the code into an OpenID.
  • WeChat Official Account (ClientTypeEnum.WX_ACCOUNT, value 203): Processes H5 logins via verify_by_wx_account, utilizing app/service/account_token.py to obtain a UnionID.

All three WeChat methods bypass password hashing, storing the raw WeChat token directly in the Identity model while binding the user record to the WeChat identifier.

Token Generation and Cryptography

Upon successful verification, the system generates a time-limited, signed token using itsdangerous.URLSafeTimedSerializer with the Flask SECRET_KEY. The generate_auth_token function in app/core/token_auth.py encapsulates three critical claims:

  • uid: The unique user identifier
  • type: The numeric ClientTypeEnum value (e.g., 100 for username, 200 for WeChat Mini-Program)
  • scope: The permission level (admin or common user)

# app/service/login_verify.py

token = generate_auth_token(
    identity['uid'],
    type.value,
    identity['scope'],
    expiration,
)

This token is returned to the client and must be presented in the Authorization header for subsequent requests.

Token Validation on Protected Routes

Protected endpoints utilize the @auth.login_required decorator from Flask-HTTPAuth. When a request arrives, the framework invokes token_auth.verify_password defined in app/core/token_auth.py, which calls verify_auth_token to decrypt and validate the signature.


# app/core/token_auth.py

@auth.verify_password
def verify_password(token, password):
    user_info = verify_auth_token(token)
    if not user_info:
        return False
    g.user = User.get_or_404(id=user_info.uid)
    return True

The decrypt_token function verifies the cryptographic signature and expiration (defaulting to 2 hours), returning the payload tuple that populates g.user for the duration of the request lifecycle.

Password Security Implementation

The Identity model in app/models/identity.py implements differentiated storage strategies based on login type. For internal logins (username, email, mobile), credentials are hashed using SHA-256 before storage:


# app/models/identity.py

if ClientTypeEnum(self.type) in current_app.config['CLINET_INNER_TYPES']:
    self._credential = hashlib.sha256(raw.encode('utf-8')).hexdigest()
else:
    self._credential = raw  # WeChat tokens stored as-is

The check_password method recomputes the hash for comparison, raising AuthFailed on mismatch to prevent timing attacks.

Practical Implementation Examples

Obtaining a Token via Email

POST /api/v1/token HTTP/1.1
Content-Type: application/json

{
  "account": "alice@example.com",
  "secret": "securePassword123",
  "type": 101
}

Response:

{
  "code": 200,
  "msg": "success",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
  }
}

Accessing Protected Resources

GET /api/v1/user/profile HTTP/1.1
Authorization: Basic <base64(token:)>

The server decodes the token, validates the signature, and loads the user into g.user before executing the view function.

Server-Side Token Decryption

from app.service.login_verify import LoginVerifyService

payload = LoginVerifyService.decrypt_token(token_string)
print(payload)

# Output: {'uid': 12, 'scope': 1, 'create_at': 1708739200, 'expire_in': 1708742800}

Summary

  • Unified Endpoint: The /v1/token route in app/api/v1/token.py handles all five login variants through a single interface.
  • Strategy Pattern: LoginVerifyService in app/service/login_verify.py dispatches to method-specific verifiers using a ClientTypeEnum mapping defined in app/libs/enums.py.
  • Cryptographic Tokens: itsdangerous provides signed, tamper-proof tokens containing user ID, login type, and scope claims.
  • Stateless Validation: The verify_password function in app/core/token_auth.py decrypts tokens without database lookups for session state.
  • Secure Storage: Internal passwords use SHA-256 hashing via app/models/identity.py, while WeChat credentials store raw tokens for API reconciliation.

Frequently Asked Questions

How does the system distinguish between different login methods?

The system uses ClientTypeEnum values passed in the type parameter (e.g., 100 for username, 101 for email, 200 for WeChat Mini-Program). The LoginVerifyService.get_token method maintains a dispatch dictionary mapping these integers to specific verification functions such as verify_by_email or verify_by_wx_mina, allowing a single endpoint to route requests appropriately.

What encryption method secures the authentication tokens?

Tokens are generated using itsdangerous.URLSafeTimedSerializer with the Flask application's SECRET_KEY. This creates a cryptographically signed string that includes a timestamp, enabling the server to detect tampering and enforce expiration (default 2 hours) without maintaining server-side session storage.

How are passwords stored for traditional login methods?

The Identity model in app/models/identity.py applies SHA-256 hashing to passwords for internal login types (username, email, mobile) before storage in the _credential field. WeChat-based logins store the raw access token instead, as these are temporary codes exchanged for persistent identifiers through Tencent's OAuth APIs.

Can the token payload be inspected without validation?

While the token format is URL-safe base64, the cryptographic signature prevents client-side tampering. Server-side decryption via decrypt_token in app/service/login_verify.py or verify_auth_token in app/core/token_auth.py validates the signature before exposing the payload containing uid, type, and scope claims.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →