OAuth 2.0 de xstarmail
xstarmail implementa el protocolo OAuth 2.0 con soporte OpenID Connect, permitiendo que aplicaciones de terceros autentiquen usuarios con su cuenta xstarmail — similar a "Iniciar sesión con Google".
OAuth 2.0
Authorization Code Flow con PKCE
OpenID Connect
Descubrimiento automático y UserInfo
Simple
Integración en minutos
1. Inicio rápido
Registra tu aplicación en la Consola de Desarrolladores. Recibirás un client_id y un client_secret.
Redirige al usuario a la página de autorización con tus parámetros.
Intercambia el código por un access token en tu backend.
Obtén los datos del usuario con el endpoint /api/oauth/userinfo.
2. Registrar aplicación
Ve a la Consola de Desarrolladores y crea una nueva aplicación. Necesitarás:
| Campo | Descripción | Requerido |
|---|---|---|
| name | Nombre de tu aplicación | ✅ |
| redirect_uris | URLs de callback autorizadas | ✅ |
| description | Descripción corta | ❌ |
| website_url | URL de tu sitio web | ❌ |
3. Flujo de autorización
xstarmail implementa el flujo Authorization Code (el más seguro para apps web):
Usuario da permiso
Recibe tokens
Datos del usuario
Usuario da permiso
Recibe tokens
Datos del usuario
Paso 1: Redirigir al usuario
Envía al usuario a la página de autorización:
https://xstarmail.es/authorize?
response_type=code
&client_id=TU_CLIENT_ID
&redirect_uri=https://tuapp.com/callback
&scope=openid profile email
&state=VALOR_ALEATORIOPaso 2: Recibir el código
Después de aprobar, el usuario vuelve a tu redirect_uri:
https://tuapp.com/callback?code=AUTHORIZATION_CODE&state=VALOR_ALEATORIOPaso 3: Intercambiar por tokens
POST /api/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&client_id=TU_CLIENT_ID
&client_secret=TU_CLIENT_SECRET
&redirect_uri=https://tuapp.com/callbackRespuesta:
{
"access_token": "eyJ...",
"refresh_token": "dGhp...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid profile email"
}Paso 4: Obtener datos del usuario
GET /api/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN{
"sub": "clxyz123abc",
"name": "María García",
"email": "maria@xstarmail.es",
"email_verified": true
}4. Endpoints
/.well-known/openid-configurationDescubrimiento OIDC — configuración automática
/authorizePágina de consentimiento del usuario
/api/oauth/authorizeProcesar decisión del usuario (aprobar/denegar)
/api/oauth/tokenIntercambiar código por tokens / renovar tokens
/api/oauth/userinfoObtener perfil del usuario autenticado
/api/oauth/revokeRevocar un access o refresh token
/api/oauth/appsListar tus aplicaciones (requiere sesión)
/api/oauth/appsCrear nueva aplicación OAuth
POST /api/oauth/token — Parámetros
| Parámetro | Tipo | Descripción |
|---|---|---|
| grant_type | string | authorization_code o refresh_token |
| code | string | Código de autorización (para grant_type=authorization_code) |
| client_id | string | ID de tu aplicación |
| client_secret | string | Secreto de tu aplicación |
| redirect_uri | string | Debe coincidir con la URI registrada |
| refresh_token | string | Token de refresco (para grant_type=refresh_token) |
| code_verifier | string | Verificador PKCE (para clientes públicos) |
GET /api/oauth/userinfo — Respuesta
| Campo | Scope | Descripción |
|---|---|---|
| sub | openid | Identificador único del usuario |
| name | profile | Nombre del usuario |
| Dirección de correo @xstarmail.es | ||
| email_verified | Siempre true |
5. Scopes
openidRequerido. Devuelve el campo sub (identificador único del usuario).
Campos: sub
profileInformación del perfil: nombre del usuario.
Campos: name, updated_at
emailDirección de correo electrónico del usuario.
Campos: email, email_verified
6. PKCE (Proof Key for Code Exchange)
Para aplicaciones públicas (SPAs, apps móviles) que no pueden guardar un client_secret de forma segura, usa PKCE:
// 1. Genera code_verifier (random string)
const verifier = crypto.randomUUID() + crypto.randomUUID();
// 2. Genera code_challenge (SHA-256 hash del verifier)
const encoder = new TextEncoder();
const digest = await crypto.subtle.digest('SHA-256', encoder.encode(verifier));
const challenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
// 3. Incluye en la URL de autorización
const authUrl = `https://xstarmail.es/authorize?
response_type=code
&client_id=TU_CLIENT_ID
&redirect_uri=https://tuapp.com/callback
&scope=openid profile email
&code_challenge=${challenge}
&code_challenge_method=S256`;
// 4. Al intercambiar el código, incluye el verifier
fetch('https://xstarmail.es/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: CODE,
client_id: 'TU_CLIENT_ID',
redirect_uri: 'https://tuapp.com/callback',
code_verifier: verifier,
}),
});8. Ejemplos de código
Node.js (Express)
const express = require('express');
const app = express();
const CLIENT_ID = 'tu_client_id';
const CLIENT_SECRET = 'tu_client_secret';
const REDIRECT_URI = 'http://localhost:3000/callback';
// Redirigir al usuario para login
app.get('/login', (req, res) => {
const state = crypto.randomUUID();
req.session.oauthState = state;
res.redirect(`https://xstarmail.es/authorize?response_type=code&client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&scope=openid profile email&state=${state}`);
});
// Callback — intercambiar código por token
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
if (state !== req.session.oauthState) {
return res.status(403).send('State mismatch');
}
// Intercambiar código por token
const tokenRes = await fetch('https://xstarmail.es/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI,
}),
});
const tokens = await tokenRes.json();
// Obtener perfil del usuario
const userRes = await fetch('https://xstarmail.es/api/oauth/userinfo', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const user = await userRes.json();
// user = { sub: "...", name: "María", email: "maria@xstarmail.es", email_verified: true }
req.session.user = user;
res.redirect('/dashboard');
});
app.listen(3000);Python (Flask)
from flask import Flask, redirect, request, session
import requests, secrets
app = Flask(__name__)
app.secret_key = 'tu-secret-key'
CLIENT_ID = 'tu_client_id'
CLIENT_SECRET = 'tu_client_secret'
REDIRECT_URI = 'http://localhost:5000/callback'
@app.route('/login')
def login():
state = secrets.token_urlsafe(32)
session['oauth_state'] = state
return redirect(
f'https://xstarmail.es/authorize?response_type=code'
f'&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}'
f'&scope=openid profile email&state={state}'
)
@app.route('/callback')
def callback():
if request.args.get('state') != session.get('oauth_state'):
return 'State mismatch', 403
# Intercambiar código por token
token_res = requests.post('https://xstarmail.es/api/oauth/token', data={
'grant_type': 'authorization_code',
'code': request.args['code'],
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'redirect_uri': REDIRECT_URI,
})
tokens = token_res.json()
# Obtener perfil
user_res = requests.get('https://xstarmail.es/api/oauth/userinfo',
headers={'Authorization': f'Bearer {tokens["access_token"]}'})
user = user_res.json()
session['user'] = user
return redirect('/dashboard')Next.js (App Router)
// app/api/auth/xstarmail/route.ts
import { redirect } from 'next/navigation';
export async function GET() {
const state = crypto.randomUUID();
// Guarda state en cookie/session para verificar después
redirect(`https://xstarmail.es/authorize?${new URLSearchParams({
response_type: 'code',
client_id: process.env.XSTARMAIL_CLIENT_ID!,
redirect_uri: process.env.XSTARMAIL_REDIRECT_URI!,
scope: 'openid profile email',
state,
})}`);
}
// app/api/auth/xstarmail/callback/route.ts
import { NextResponse } from 'next/server';
export async function GET(req: Request) {
const url = new URL(req.url);
const code = url.searchParams.get('code');
const tokenRes = await fetch('https://xstarmail.es/api/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code!,
client_id: process.env.XSTARMAIL_CLIENT_ID!,
client_secret: process.env.XSTARMAIL_CLIENT_SECRET!,
redirect_uri: process.env.XSTARMAIL_REDIRECT_URI!,
}),
});
const tokens = await tokenRes.json();
const userRes = await fetch('https://xstarmail.es/api/oauth/userinfo', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const user = await userRes.json();
// Crear sesión con los datos del usuario
const response = NextResponse.redirect(new URL('/dashboard', req.url));
// ... guardar user en cookie/sesión
return response;
}9. Códigos de error
| Error | HTTP | Descripción |
|---|---|---|
| invalid_request | 400 | Falta un parámetro obligatorio o formato incorrecto |
| invalid_client | 400 | Client ID no encontrado o client_secret incorrecto |
| invalid_grant | 400 | Código expirado, ya usado, o redirect_uri no coincide |
| invalid_scope | 400 | Scope solicitado no válido |
| unsupported_response_type | 400 | Solo se soporta response_type=code |
| unsupported_grant_type | 400 | Solo authorization_code y refresh_token |
| access_denied | — | El usuario denegó la autorización |
| invalid_token | 401 | Token expirado, revocado o inválido |
| login_required | 401 | El usuario no tiene sesión activa |
10. Seguridad
🔒 HTTPS obligatorio
Todas las redirect URIs en producción deben usar HTTPS. Solo se permite HTTP para localhost durante desarrollo.
🎲 State parameter
Siempre usa un valor aleatorio en state y verifica que coincida en el callback para prevenir ataques CSRF.
🔑 Protege tu client_secret
Nunca expongas el client_secret en código frontend. Usa PKCE para apps públicas (SPAs, apps móviles).
⏱️ Tiempos de vida
Los authorization codes expiran en 10 minutos y solo se pueden usar una vez. Los access tokens duran 1 hora. Los refresh tokens duran 30 días.
🔄 Rotación de refresh tokens
Cada vez que se usa un refresh token, se revoca y se emite uno nuevo para mejorar la seguridad.
xstarmail OAuth 2.0 — Consola de Desarrolladores · xstarmail.es