Minimalist SDK for NiceLogin: JWT verification, password reset, activate/deactivate user (zero dependencies)
Project description
NiceLogin Python SDK
🤖 LLM-Friendly SDK: This SDK was designed to be easily implemented by code agents (Claude, GPT, Cursor, Copilot, etc). Just ask your code agent to read this README and it will be able to implement complete authentication in your project.
Minimalist SDK for NiceLogin. Zero external dependencies.
Installation
pip install nicelogin-jwks-python
Important Concepts
api_key vs api_secret
| Credential | Where to use | Purpose |
|---|---|---|
api_key |
Frontend/Backend | Public operations: login, registration, password reset |
api_secret |
Backend only | Administrative operations: activate/deactivate users |
⚠️ NEVER expose api_secret in the frontend!
Two Main Classes
| Class | Purpose |
|---|---|
NiceLogin |
HTTP client to call the API (password reset, activate user, etc) |
NiceLoginJWKS |
Local JWT token verifier (no HTTP requests) |
Complete Usage Guide
1. Initialization
from nicelogin_jwks import NiceLogin, NiceLoginJWKS, NiceLoginError
# Client for API operations
client = NiceLogin(
api_key="nicelogin_your_api_key", # Required
api_secret="your_api_secret", # Optional (admin only)
base_url="https://api.v1.nicelogin.com" # Optional (default)
)
2. Password Reset (Complete Flow)
Scenario: User forgot password
# STEP 1: Backend requests reset token
reset_token = client.request_password_reset("user@email.com")
# STEP 2: Send reset_token via email to user
# (implement your own email sending)
send_email(
to="user@email.com",
subject="Password Reset",
body=f"Use this token to reset your password: {reset_token}"
)
# STEP 3: User clicks link and provides new password
# Backend receives token and new password from frontend
client.reset_password(
email="user@email.com",
current_password="current_password", # User needs to know current password
new_password="new_password_123",
reset_token=reset_token
)
3. Change Password (Logged In User)
Scenario: User wants to change password knowing the current one
# Single line - requests token internally
client.change_password(
email="user@email.com",
current_password="current_password",
new_password="new_password_123"
)
4. Activate/Deactivate User (Admin)
⚠️ Requires api_secret - Use only in backend!
# Initialize with api_secret
admin_client = NiceLogin(
api_key="nicelogin_xxx",
api_secret="secret_xxx" # REQUIRED for these operations
)
# Deactivate user (e.g., ban, suspend)
admin_client.deactivate_user("550e8400-e29b-41d4-a716-446655440000")
# Reactivate user
admin_client.activate_user("550e8400-e29b-41d4-a716-446655440000")
5. Verify JWT Token (Local, No HTTP)
Scenario: Validate token on each request in your backend
import urllib.request
import json
# STEP 1: Fetch JWKS once (cache for 24h)
api_key = "nicelogin_your_api_key"
jwks_url = f"https://api.v1.nicelogin.com/.well-known/jwks/{api_key}"
with urllib.request.urlopen(jwks_url) as response:
jwks = json.loads(response.read().decode())
# STEP 2: Create verifier (do this once at initialization)
verifier = NiceLoginJWKS(jwks)
# STEP 3: Verify tokens on each request
def authenticate(request):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
# Check if token is valid (True/False)
if not verifier.verify_token(token):
raise Exception("Invalid token")
# Extract user data
payload = verifier.unpack(token)
return {
"user_id": payload["sub"],
"email": payload["email"],
"company_id": payload["company_id"]
}
Quick API Reference
NiceLogin Class
client = NiceLogin(api_key, api_secret=None, base_url="https://api.v1.nicelogin.com")
| Method | Parameters | Return | Description |
|---|---|---|---|
request_password_reset(email) |
email: str |
str (token) |
Request reset token |
reset_password(email, current_password, new_password, reset_token) |
all str |
bool |
Reset password with token |
change_password(email, current_password, new_password) |
all str |
bool |
Change password (1 step) |
activate_user(user_id) |
user_id: str |
bool |
Activate user* |
deactivate_user(user_id) |
user_id: str |
bool |
Deactivate user* |
*Requires api_secret
NiceLoginJWKS Class
verifier = NiceLoginJWKS(jwks) # jwks is dict from /.well-known/jwks/{api_key} endpoint
| Method | Parameters | Return | Description |
|---|---|---|---|
verify_token(token) |
token: str |
bool |
True if valid |
unpack(token, verify=True) |
token: str, verify: bool |
dict |
Extract payload |
Token Payload (claims)
{
"sub": "550e8400-...", # User ID (UUID)
"email": "user@email.com", # User email
"company_id": "660e8400-...", # Company ID (UUID)
"exp": 1704153600, # Expiration (Unix timestamp)
"iat": 1704067200, # Issued at (Unix timestamp)
"jti": "unique-token-id", # Unique token ID
"sid": "session-id", # Session ID (optional)
"user_data": {"name": "John"} # Custom data (optional)
}
Error Handling
from nicelogin_jwks import NiceLogin, NiceLoginError
client = NiceLogin(api_key="...")
try:
client.change_password("user@email.com", "wrong_password", "new_pass")
except NiceLoginError as e:
print(f"Error {e.status}: {e.message}")
# Error 401: Invalid credentials
Common Error Codes
| Status | Meaning |
|---|---|
| 400 | Invalid data (weak password, invalid email) |
| 401 | Invalid credentials (api_key, password, token) |
| 404 | User not found |
| 500 | Internal server error |
Integration Examples
FastAPI
from fastapi import FastAPI, Depends, HTTPException, Header
from nicelogin_jwks import NiceLogin, NiceLoginJWKS, NiceLoginError
import urllib.request
import json
app = FastAPI()
# Configuration
API_KEY = "nicelogin_xxx"
API_SECRET = "secret_xxx"
# Initialize client
client = NiceLogin(api_key=API_KEY, api_secret=API_SECRET)
# Load JWKS once
with urllib.request.urlopen(f"https://api.v1.nicelogin.com/.well-known/jwks/{API_KEY}") as r:
jwks = json.loads(r.read().decode())
verifier = NiceLoginJWKS(jwks)
# Authentication dependency
def get_current_user(authorization: str = Header(...)):
token = authorization.replace("Bearer ", "")
if not verifier.verify_token(token):
raise HTTPException(401, "Invalid token")
return verifier.unpack(token)
# Routes
@app.post("/reset-password")
def reset_password(email: str):
try:
token = client.request_password_reset(email)
# Send token via email...
return {"message": "Email sent"}
except NiceLoginError as e:
raise HTTPException(e.status, e.message)
@app.get("/me")
def get_me(user: dict = Depends(get_current_user)):
return {"user_id": user["sub"], "email": user["email"]}
@app.post("/admin/deactivate/{user_id}")
def deactivate(user_id: str, admin: dict = Depends(get_current_user)):
client.deactivate_user(user_id)
return {"message": "User deactivated"}
Flask
from flask import Flask, request, jsonify
from nicelogin_jwks import NiceLogin, NiceLoginJWKS, NiceLoginError
from functools import wraps
import urllib.request
import json
app = Flask(__name__)
# Configuration
API_KEY = "nicelogin_xxx"
client = NiceLogin(api_key=API_KEY)
# Load JWKS
with urllib.request.urlopen(f"https://api.v1.nicelogin.com/.well-known/jwks/{API_KEY}") as r:
jwks = json.loads(r.read().decode())
verifier = NiceLoginJWKS(jwks)
# Authentication decorator
def require_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if not verifier.verify_token(token):
return jsonify({"error": "Invalid token"}), 401
request.user = verifier.unpack(token)
return f(*args, **kwargs)
return decorated
@app.route("/me")
@require_auth
def get_me():
return jsonify({"user_id": request.user["sub"], "email": request.user["email"]})
Django
# middleware.py
from nicelogin_jwks import NiceLoginJWKS
import urllib.request
import json
API_KEY = "nicelogin_xxx"
# Load JWKS once
with urllib.request.urlopen(f"https://api.v1.nicelogin.com/.well-known/jwks/{API_KEY}") as r:
jwks = json.loads(r.read().decode())
verifier = NiceLoginJWKS(jwks)
class NiceLoginMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if token and verifier.verify_token(token):
request.nicelogin_user = verifier.unpack(token)
else:
request.nicelogin_user = None
return self.get_response(request)
# views.py
from django.http import JsonResponse
def me(request):
if not request.nicelogin_user:
return JsonResponse({"error": "Not authenticated"}, status=401)
return JsonResponse({
"user_id": request.nicelogin_user["sub"],
"email": request.nicelogin_user["email"]
})
Implementation Checklist
For LLMs and developers, use this checklist:
- Install:
pip install nicelogin-jwks-python - Get
api_keyfrom NiceLogin dashboard - Get
api_secret(if you need to activate/deactivate users) - Implement password reset endpoint
- Implement JWT token verification on protected routes
- Cache JWKS for 24h (don't fetch on every request)
- Handle errors with try/except
NiceLoginError
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nicelogin_jwks_python-0.2.1.tar.gz.
File metadata
- Download URL: nicelogin_jwks_python-0.2.1.tar.gz
- Upload date:
- Size: 10.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c965aa3b2862d952369857ff16f70fb5f8d35e78aac953d6bc22ed8388b8778
|
|
| MD5 |
d4f5ae08289ca71e6280ade1277f8906
|
|
| BLAKE2b-256 |
3bba3ca3703015e2635621e03d33b64ed9fb16eb60778293fb96731071f706fe
|
File details
Details for the file nicelogin_jwks_python-0.2.1-py3-none-any.whl.
File metadata
- Download URL: nicelogin_jwks_python-0.2.1-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ae293dac02f2ac98088b28d0289755cbbfff33fa9c1281f33b59db515fd5b72
|
|
| MD5 |
f05f1bed3ff89f857a97cf842e5e0413
|
|
| BLAKE2b-256 |
7720ab217bcae274b9c4abbaa96f49c466216b2488fe314970b8512854853584
|