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-configurationEsta 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.
La configuración
Sección titulada «La configuración»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', }, ],});Los dos valores que hay que cambiar
Sección titulada «Los dos valores que hay que cambiar»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.
pkce: true
Sección titulada «pkce: true»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.
El botón
Sección titulada «El botón»await authClient.signIn.oauth2({ providerId: 'costia', callbackURL: '/',});Better Auth recibirá la respuesta en
https://tu-dominio/api/auth/callback/costia.
Qué recibes
Sección titulada «Qué recibes»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.
Si la persona desmarca algo
Sección titulada «Si la persona desmarca algo»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.
