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, value100) - Email (
ClientTypeEnum.EMAIL, value101) - Mobile (
ClientTypeEnum.MOBILE, value102)
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, value200): Usesverify_by_wx_minato exchange thecodefor an OpenID viaapp/service/wx_token.py, creating or fetching the user viaUserDao.register_by_wx_mina. - WeChat Open Platform (
ClientTypeEnum.WX_OPEN, value202): Handles web QR logins throughverify_by_wx_open, callingapp/service/open_token.pyto resolve thecodeinto an OpenID. - WeChat Official Account (
ClientTypeEnum.WX_ACCOUNT, value203): Processes H5 logins viaverify_by_wx_account, utilizingapp/service/account_token.pyto 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 identifiertype: The numericClientTypeEnumvalue (e.g.,100for username,200for 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/tokenroute inapp/api/v1/token.pyhandles all five login variants through a single interface. - Strategy Pattern:
LoginVerifyServiceinapp/service/login_verify.pydispatches to method-specific verifiers using aClientTypeEnummapping defined inapp/libs/enums.py. - Cryptographic Tokens:
itsdangerousprovides signed, tamper-proof tokens containing user ID, login type, and scope claims. - Stateless Validation: The
verify_passwordfunction inapp/core/token_auth.pydecrypts 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →