🛠️ Developer Documentation
Complete guide to integrating "Login with Rizzzler" into your application with a secure OAuth 2.0 flow.
⚡ Quick Start (5 Minutes)
Step 1: Register Your Application
Contact the Rizzzler admin to register your app. You'll receive:
CLIENT_ID— public identifierCLIENT_SECRET— keep this secret on your backendREDIRECT_URI— where users return after login
Step 2: Add the login button
<a href="https://www.rizzzler.work.gd/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://yourapp.com/auth/callback&response_type=code&state=RANDOM_STATE&scope=profile+email+avatar">
<button>Login with Rizzzler</button>
</a>
Step 3: Handle the callback
When the user approves, they're redirected back to your app with a code:
https://yourapp.com/auth/callback?code=AUTH_CODE&state=STATE
Step 4: Exchange the code for a token
POST https://www.rizzzler.work.gd/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTH_CODE&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET&
redirect_uri=https://yourapp.com/auth/callback
Step 5: Fetch user info
GET https://www.rizzzler.work.gd/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
📚 Complete Implementation Guide
What is OAuth 2.0?
OAuth 2.0 is an industry-standard way to let users sign in without sharing passwords. With Rizzzler, users approve a request and your app receives only the data it needs.
Authorization code flow
- User clicks the Rizzzler sign-in button.
- Your app redirects to the Rizzzler authorization screen.
- The user approves the requested scopes.
- Rizzzler sends a code back to your callback URL.
- Your backend exchanges the code for an access token.
- Your backend requests user data from the userinfo endpoint.
Key security features
- 🔐 User passwords are never shared.
- 🔓 Users can revoke access anytime.
- ⏰ Authorization codes expire quickly.
- 🛡️ Access tokens expire after 30 days.
- ✅ A
stateparameter prevents CSRF issues.
Scopes explained
| Scope | What it gives access to |
|---|---|
profile |
Username and display name |
email |
User email address |
avatar |
Profile picture URL |
State parameter
The state value helps prevent CSRF attacks by letting you compare a server-side value before accepting the callback.
- Generate a random string in your backend
- Store it in the user session
- Pass it to the authorization request
- Validate it after the callback
🔌 OAuth Endpoints
Redirects the user to the Rizzzler login/approval page.
GET https://www.rizzzler.work.gd/oauth/authorize?
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
response_type=code&
state=RANDOM_STRING&
scope=profile email avatar
| Parameter | Required | Description |
|---|---|---|
client_id |
Yes | Your app's public ID |
redirect_uri |
Yes | Must exactly match a registered callback URL |
response_type |
Yes | Always code |
state |
Yes | Randomized value for CSRF validation |
scope |
No | Space-separated list like profile email avatar |
redirect_uri?code=CODE&state=STATE; if denied, your app receives error=access_denied.
Exchange the authorization code for an access token on your backend.
POST https://www.rizzzler.work.gd/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTH_CODE&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET&
redirect_uri=https://yourapp.com/callback
{
"access_token": "long_token_string_here",
"token_type": "Bearer",
"expires_in": 2592000,
"scope": "profile email avatar"
}
Fetch user profile data using the access token.
GET https://www.rizzzler.work.gd/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
{
"sub": "user_mongodb_id",
"username": "john_doe",
"name": "John Doe",
"email": "john@example.com",
"picture": "https://cdn.example.com/avatars/john.jpg"
}
💻 Code Examples
Node.js + Express
const express = require('express');
const crypto = require('crypto');
const fetch = require('node-fetch');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.get('/login', (req, res) => {
const state = crypto.randomBytes(32).toString('hex');
req.session.oauthState = state;
const params = new URLSearchParams({
client_id: process.env.RIZZZLER_CLIENT_ID,
redirect_uri: 'http://localhost:3000/auth/callback',
response_type: 'code',
state,
scope: 'profile email avatar'
});
res.redirect(`https://www.rizzzler.work.gd/oauth/authorize?${params}`);
});
app.get('/auth/callback', async (req, res) => {
const { code, state } = req.query;
if (state !== req.session.oauthState) {
return res.status(403).send('CSRF token mismatch');
}
const tokenRes = await fetch('https://www.rizzzler.work.gd/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: process.env.RIZZZLER_CLIENT_ID,
client_secret: process.env.RIZZZLER_CLIENT_SECRET,
redirect_uri: 'http://localhost:3000/auth/callback'
})
});
const tokenData = await tokenRes.json();
const userRes = await fetch('https://www.rizzzler.work.gd/oauth/userinfo', {
headers: { Authorization: `Bearer ${tokenData.access_token}` }
});
const user = await userRes.json();
req.session.user = user;
res.redirect('/dashboard');
});
Python + Flask
from flask import Flask, redirect, request, session
import requests
from secrets import token_urlsafe
from urllib.parse import urlencode
app = Flask(__name__)
app.secret_key = 'your-secret-key'
@app.route('/login')
def login():
state = token_urlsafe(32)
session['oauth_state'] = state
params = {
'client_id': 'YOUR_CLIENT_ID',
'redirect_uri': 'http://localhost:5000/auth/callback',
'response_type': 'code',
'state': state,
'scope': 'profile email avatar'
}
return redirect(f'https://www.rizzzler.work.gd/oauth/authorize?{urlencode(params)}')
@app.route('/auth/callback')
def callback():
code = request.args.get('code')
state = request.args.get('state')
if state != session.get('oauth_state'):
return 'CSRF token mismatch', 403
token_res = requests.post('https://www.rizzzler.work.gd/oauth/token', data={
'grant_type': 'authorization_code',
'code': code,
'client_id': 'YOUR_CLIENT_ID',
'client_secret': 'YOUR_CLIENT_SECRET',
'redirect_uri': 'http://localhost:5000/auth/callback'
})
token_data = token_res.json()
user_res = requests.get(
'https://www.rizzzler.work.gd/oauth/userinfo',
headers={'Authorization': f"Bearer {token_data['access_token']}"}
)
session['user'] = user_res.json()
return redirect('/dashboard')
JavaScript (frontend reference only)
// Never put CLIENT_SECRET in frontend code.
function loginWithRizzzler() {
const state = crypto.getRandomValues(new Uint8Array(32))
.reduce((acc, byte) => acc + byte.toString(16).padStart(2, '0'), '');
const params = new URLSearchParams({
client_id: 'YOUR_CLIENT_ID',
redirect_uri: 'https://yourapp.com/auth/callback',
response_type: 'code',
state,
scope: 'profile email avatar'
});
window.location.href = `https://www.rizzzler.work.gd/oauth/authorize?${params}`;
}
⚠️ Error Handling
Common errors and fixes
| Error | Cause | Fix |
|---|---|---|
invalid_client |
Bad client ID or secret | Verify the credentials and keep them in environment variables |
invalid_grant |
Expired code or redirect mismatch | Ensure the callback URL matches exactly |
access_denied |
User rejected access | Handle gracefully and offer an alternative login path |
invalid_token |
Expired or invalid token | Ask the user to log in again |
Typical callback logic
const { code, error, error_description, state } = req.query;
if (error) {
return res.redirect(`/login?error=${encodeURIComponent(error)}`);
}
if (!code) {
return res.status(400).send('Missing authorization code');
}
if (state !== req.session.oauthState) {
return res.status(403).send('CSRF validation failed');
}
const response = await fetch('https://www.rizzzler.work.gd/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: process.env.RIZZZLER_CLIENT_ID,
client_secret: process.env.RIZZZLER_CLIENT_SECRET,
redirect_uri: 'https://yourapp.com/auth/callback'
})
});
🔐 Security Best Practices
Do this
- ✅ Keep
CLIENT_SECRETin environment variables. - ✅ Exchange the auth code on the backend only.
- ✅ Verify the
stateparameter. - ✅ Use HTTPS in production.
- ✅ Store tokens securely and rotate them when needed.
Do not do this
- ❌ Never expose the client secret in browser code.
- ❌ Never skip state validation.
- ❌ Never store tokens in localStorage.
- ❌ Never reuse authorization codes.
- ❌ Never trust client-supplied values without validation.
📚 Additional Resources
🎉 Ready to integrate?
Contact the team to register your app and get your client credentials.