Ir al contenido

Iniciar sesión con Costia

Costia es un proveedor OpenID Connect estándar. Si tu librería lee un documento de discovery, no tienes que copiar ninguna URL:

https://id.costia.app/.well-known/openid-configuration

Esta página usa Better Auth porque es lo que usa Ambrosia, la tienda de ejemplo, y el código que verás es el que está en producción. Con otra librería cambia la forma, no el fondo.

lib/auth/server.js
import { genericOAuth } from 'better-auth/plugins';
genericOAuth({
config: [
{
providerId: 'costia',
clientId: process.env.COSTIA_CLIENT_ID,
clientSecret: process.env.COSTIA_CLIENT_SECRET,
discoveryUrl: 'https://id.costia.app/.well-known/openid-configuration',
scopes: ['openid', 'profile', 'email', 'invoices:write'],
pkce: true,
authentication: 'basic',
},
],
});

Son los dos únicos ajustes que este plugin trae por defecto de una forma que no puede funcionar contra Costia. Ambos fallan en momentos distintos y ninguno de los dos dice lo que pasa.

El plugin trae false. Costia registra todos los clientes exigiendo PKCE, así que una petición sin code_challenge se rechaza mientras se valida: antes de la pantalla de login y antes del consentimiento. Tu usuario vuelve a tu sitio con un error en la query que el plugin no muestra.

Lo que ves es que pulsar el botón no hace nada. Ni login, ni consentimiento, ni mensaje. Todos los síntomas son la ausencia de algo.

authentication: 'basic' (clientes confidenciales)

Sección titulada «authentication: 'basic' (clientes confidenciales)»

El plugin trae 'post', que envía las credenciales en el cuerpo. Tu cliente está registrado con client_secret_basic, y Spring comprueba el método con el que se registró el cliente, no cualquiera que el servidor soporte.

Esto falla más tarde: la persona completa el login y el consentimiento, y el intercambio de token —que ocurre servidor a servidor, donde no lo ves— devuelve invalid_client. Parece un problema nuevo y sin relación con el anterior.

Esta configuración corresponde a Better Auth ejecutándose en tu servidor. Una app nativa se registra como cliente público, no lleva secreto y entrega client_id y code_verifier al intercambiar el código.

await authClient.signIn.oauth2({
providerId: 'costia',
callbackURL: '/',
});

Better Auth recibirá la respuesta en https://tu-dominio/api/auth/callback/costia.

Un id_token con:

Claim Cuándo Qué es
sub siempre El identificador de la persona en Costia. Estable, y no es su correo
name con profile Su nombre completo
email con email Su correo
email_verified con email true; un correo sin verificar no se publica

Entrar con Google verifica el correo que Google declara. Apple solo lo verifica cuando Apple comparte ese correo. Crear una cuenta con email y contraseña, o añadir un correo manualmente después de entrar con Apple, no lo verifica. Se puede usar Costia así, pero el scope email queda bloqueado hasta verificarlo mediante un proveedor.

La pantalla de consentimiento no trae nada premarcado salvo la identificación. Es una exigencia del RGPD —el Considerando 32 nombra las casillas premarcadas como algo que no constituye consentimiento— y significa que tu cliente puede entrar sin concederte el correo, o sin dejarte escribir facturas.

Al desmarcar algo se le dice explícitamente qué dejará de poder hacer tu aplicación, así que es una decisión informada y no un descuido. Tu código tiene que aguantarlo: un email ausente no es un error, y un 403 al escribir una factura significa que declinó, no que algo esté roto.