@dacyti-gsi/auth 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/package.json +1 -1
- package/src/core.ts +9 -1
- package/src/index.ts +1 -0
- package/src/types.ts +13 -0
package/README.md
CHANGED
|
@@ -88,6 +88,48 @@ El portal solo redirige a URIs registradas, así que sin ella la sesión se cier
|
|
|
88
88
|
igual pero el navegador se queda en el portal. Si la registras, indícala también
|
|
89
89
|
en `postLogoutRedirectUri`.
|
|
90
90
|
|
|
91
|
+
## Declarar permisos y roles desde el código (`sso.sync()`)
|
|
92
|
+
|
|
93
|
+
El catálogo de la app vive en su código y se declara solo en el portal, sin copiarlo
|
|
94
|
+
a mano a la consola:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// lib/sso.ts
|
|
98
|
+
export const sso = defineNextSSO({
|
|
99
|
+
permissions: ["pedidos:ver", "pedidos:anular"] as const,
|
|
100
|
+
roles: {
|
|
101
|
+
gerente: ["pedidos:ver", "pedidos:anular"],
|
|
102
|
+
lector: ["pedidos:ver"],
|
|
103
|
+
},
|
|
104
|
+
// Lo que la consola muestra al decidir a quién dar cada cosa.
|
|
105
|
+
descriptions: {
|
|
106
|
+
permissions: { "pedidos:anular": "Anular pedidos ya confirmados" },
|
|
107
|
+
roles: { gerente: "Gestiona los pedidos de su sucursal" },
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
// instrumentation.ts
|
|
114
|
+
export async function register() {
|
|
115
|
+
if (process.env.NEXT_RUNTIME !== "nodejs") return;
|
|
116
|
+
const { sso } = await import("@/lib/sso");
|
|
117
|
+
await sso.sync().catch((error) => console.warn("[sso] sin sincronizar:", error));
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- Los permisos de cada rol salen del tipo de `permissions`, y las claves de
|
|
122
|
+
`descriptions` también: una errata no compila.
|
|
123
|
+
- Es idempotente y solo toca el catálogo de este `clientId` (autentica con las mismas
|
|
124
|
+
credenciales del cliente, `client_secret_basic`). Se puede llamar en cada arranque.
|
|
125
|
+
- Declara el catálogo; **no asigna roles a cuentas**, eso se decide en la consola.
|
|
126
|
+
- No borra: un permiso o rol que quitas del código sigue en la consola hasta que
|
|
127
|
+
alguien lo elimina allí.
|
|
128
|
+
- Todo o nada: si un rol pide un permiso que no está en el catálogo, el portal rechaza
|
|
129
|
+
el manifiesto entero y `sync()` lanza con el motivo.
|
|
130
|
+
- Un rol que incluya `sso:asignar-roles` permite a su titular asignar roles de esta
|
|
131
|
+
app desde su cuenta, sin ser administrador del portal.
|
|
132
|
+
|
|
91
133
|
## Renovar el acceso (`proxy.ts`)
|
|
92
134
|
|
|
93
135
|
El acceso dura 15 minutos. Los Server Components no pueden escribir cookies, así que
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -460,6 +460,10 @@ export async function syncManifest(input: {
|
|
|
460
460
|
clientSecret: string;
|
|
461
461
|
permissions: readonly string[];
|
|
462
462
|
roles: Readonly<Record<string, readonly string[]>>;
|
|
463
|
+
descriptions?: {
|
|
464
|
+
permissions?: Readonly<Record<string, string | undefined>>;
|
|
465
|
+
roles?: Readonly<Record<string, string | undefined>>;
|
|
466
|
+
};
|
|
463
467
|
}): Promise<{ permissions: number; roles: number }> {
|
|
464
468
|
const endpoint = new URL("/api/apps/manifest", input.issuer);
|
|
465
469
|
const credentials = Buffer.from(
|
|
@@ -473,10 +477,14 @@ export async function syncManifest(input: {
|
|
|
473
477
|
"content-type": "application/json",
|
|
474
478
|
},
|
|
475
479
|
body: JSON.stringify({
|
|
476
|
-
permissions: input.permissions.map((key) => ({
|
|
480
|
+
permissions: input.permissions.map((key) => ({
|
|
481
|
+
key,
|
|
482
|
+
description: input.descriptions?.permissions?.[key],
|
|
483
|
+
})),
|
|
477
484
|
roles: Object.entries(input.roles).map(([name, permissions]) => ({
|
|
478
485
|
name,
|
|
479
486
|
permissions,
|
|
487
|
+
description: input.descriptions?.roles?.[name],
|
|
480
488
|
})),
|
|
481
489
|
}),
|
|
482
490
|
});
|
package/src/index.ts
CHANGED
|
@@ -658,6 +658,7 @@ export function defineSSO<const Permission extends string = string>(
|
|
|
658
658
|
clientSecret: resolved.clientSecret,
|
|
659
659
|
permissions: resolved.permissions ?? [],
|
|
660
660
|
roles: resolved.roles ?? {},
|
|
661
|
+
descriptions: resolved.descriptions,
|
|
661
662
|
}),
|
|
662
663
|
|
|
663
664
|
async handleRequest(request) {
|
package/src/types.ts
CHANGED
|
@@ -58,6 +58,19 @@ export type SSOClientOptions<Permission extends string = string> = {
|
|
|
58
58
|
*/
|
|
59
59
|
roles?: Readonly<Record<string, readonly Permission[]>>;
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Descripciones legibles de permisos y roles, para la consola del portal.
|
|
63
|
+
*
|
|
64
|
+
* `sso.sync()` las envía junto al manifiesto; sin ellas la consola solo muestra
|
|
65
|
+
* claves como `pedidos:anular`, que no dicen qué concede cada una a quien debe
|
|
66
|
+
* decidir a quién dárselo. Las claves de `permissions` salen del mismo tipo que
|
|
67
|
+
* `permissions`: una descripción para un permiso que no existe no compila.
|
|
68
|
+
*/
|
|
69
|
+
descriptions?: {
|
|
70
|
+
permissions?: Partial<Record<Permission, string>>;
|
|
71
|
+
roles?: Readonly<Record<string, string>>;
|
|
72
|
+
};
|
|
73
|
+
|
|
61
74
|
/** Ruta donde se montan los handlers. Por defecto `/api/sso`. */
|
|
62
75
|
basePath?: string;
|
|
63
76
|
|