⚡ SDK-ready OAuth docs

🛠️ 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 identifier
  • CLIENT_SECRET — keep this secret on your backend
  • REDIRECT_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
Response: the user's profile data, including username, email, and avatar URL.

📚 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

  1. User clicks the Rizzzler sign-in button.
  2. Your app redirects to the Rizzzler authorization screen.
  3. The user approves the requested scopes.
  4. Rizzzler sends a code back to your callback URL.
  5. Your backend exchanges the code for an access token.
  6. 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 state parameter 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

1. Authorization endpoint

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 response: on approval you receive redirect_uri?code=CODE&state=STATE; if denied, your app receives error=access_denied.
2. Token endpoint

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
Success response:
{
  "access_token": "long_token_string_here",
  "token_type": "Bearer",
  "expires_in": 2592000,
  "scope": "profile email avatar"
}
3. Userinfo endpoint

Fetch user profile data using the access token.

GET https://www.rizzzler.work.gd/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
Success response:
{
  "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}`;
}
Important: never expose the client secret to a browser. Always complete the token exchange on your backend.

⚠️ 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_SECRET in environment variables.
  • ✅ Exchange the auth code on the backend only.
  • ✅ Verify the state parameter.
  • ✅ 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.