From aebe9ae148357ba6ce7913ff1a3528153fcbed81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:58:53 +0900 Subject: [PATCH] =?UTF-8?q?enhance(SSO):=20SAML=E8=AA=8D=E8=A8=BC=E3=81=A7?= =?UTF-8?q?HTTP-POST=E3=83=90=E3=82=A4=E3=83=B3=E3=83=87=E3=82=A3=E3=83=B3?= =?UTF-8?q?=E3=82=B0=E3=81=AB=E5=AF=BE=E5=BF=9C=20(MisskeyIO#531)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/1710416761960-single-sign-on.js | 15 ---- .../migration/1710667213868-single-sign-on.js | 21 ++++++ .../src/models/SingleSignOnServiceProvider.ts | 6 ++ .../api/endpoints/admin/indie-auth/create.ts | 2 +- .../api/endpoints/admin/indie-auth/delete.ts | 2 +- .../api/endpoints/admin/indie-auth/list.ts | 2 +- .../api/endpoints/admin/indie-auth/update.ts | 2 +- .../server/api/endpoints/admin/sso/create.ts | 10 ++- .../server/api/endpoints/admin/sso/delete.ts | 2 +- .../server/api/endpoints/admin/sso/list.ts | 8 +- .../server/api/endpoints/admin/sso/update.ts | 4 +- .../server/sso/JWTIdentifyProviderService.ts | 75 +++++++------------ .../server/sso/SAMLIdentifyProviderService.ts | 65 +++++++++------- .../frontend/src/pages/admin/security.vue | 8 +- packages/frontend/src/pages/sso.vue | 62 +++++++++++++-- packages/misskey-js/src/autogen/types.ts | 8 ++ 16 files changed, 185 insertions(+), 107 deletions(-) delete mode 100644 packages/backend/migration/1710416761960-single-sign-on.js create mode 100644 packages/backend/migration/1710667213868-single-sign-on.js diff --git a/packages/backend/migration/1710416761960-single-sign-on.js b/packages/backend/migration/1710416761960-single-sign-on.js deleted file mode 100644 index a24d3aab6..000000000 --- a/packages/backend/migration/1710416761960-single-sign-on.js +++ /dev/null @@ -1,15 +0,0 @@ -export class SingleSignOn1710416761960 { - name = 'SingleSignOn1710416761960' - - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "public"."sso_service_provider_type_enum" AS ENUM('saml', 'jwt')`); - await queryRunner.query(`CREATE TABLE "sso_service_provider" ("id" character varying(36) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(256), "type" "public"."sso_service_provider_type_enum" NOT NULL, "issuer" character varying(512) NOT NULL, "audience" character varying(512) array NOT NULL DEFAULT '{}', "acsUrl" character varying(512) NOT NULL, "publicKey" character varying(4096) NOT NULL, "privateKey" character varying(4096), "signatureAlgorithm" character varying(100) NOT NULL, "cipherAlgorithm" character varying(100), "wantAuthnRequestsSigned" boolean NOT NULL DEFAULT false, "wantAssertionsSigned" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_0e5fff64534026e48e1c248991a" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_86eee7fa4ae68e4a558dc50961" ON "sso_service_provider" ("createdAt") `); - } - - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_86eee7fa4ae68e4a558dc50961"`); - await queryRunner.query(`DROP TABLE "sso_service_provider"`); - await queryRunner.query(`DROP TYPE "public"."sso_service_provider_type_enum"`); - } -} diff --git a/packages/backend/migration/1710667213868-single-sign-on.js b/packages/backend/migration/1710667213868-single-sign-on.js new file mode 100644 index 000000000..0f2095549 --- /dev/null +++ b/packages/backend/migration/1710667213868-single-sign-on.js @@ -0,0 +1,21 @@ +export class SingleSignOn1710667213868 { + name = 'SingleSignOn1710667213868' + + async up(queryRunner) { + await queryRunner.query(`DROP TABLE IF EXISTS "sso_service_provider"`); + await queryRunner.query(`DROP INDEX IF EXISTS "public"."IDX_86eee7fa4ae68e4a558dc50961"`); + await queryRunner.query(`DROP TYPE IF EXISTS "public"."sso_service_provider_binding_enum"`); + await queryRunner.query(`DROP TYPE IF EXISTS "public"."sso_service_provider_type_enum"`); + await queryRunner.query(`CREATE TYPE "public"."sso_service_provider_type_enum" AS ENUM('saml', 'jwt')`); + await queryRunner.query(`CREATE TYPE "public"."sso_service_provider_binding_enum" AS ENUM('post', 'redirect')`); + await queryRunner.query(`CREATE TABLE "sso_service_provider" ("id" character varying(36) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "name" character varying(256), "type" "public"."sso_service_provider_type_enum" NOT NULL, "issuer" character varying(512) NOT NULL, "audience" character varying(512) array NOT NULL DEFAULT '{}', "binding" "public"."sso_service_provider_binding_enum" NOT NULL, "acsUrl" character varying(512) NOT NULL, "publicKey" character varying(4096) NOT NULL, "privateKey" character varying(4096), "signatureAlgorithm" character varying(100) NOT NULL, "cipherAlgorithm" character varying(100), "wantAuthnRequestsSigned" boolean NOT NULL DEFAULT false, "wantAssertionsSigned" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_0e5fff64534026e48e1c248991a" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_86eee7fa4ae68e4a558dc50961" ON "sso_service_provider" ("createdAt") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_86eee7fa4ae68e4a558dc50961"`); + await queryRunner.query(`DROP TABLE "sso_service_provider"`); + await queryRunner.query(`DROP TYPE "public"."sso_service_provider_binding_enum"`); + await queryRunner.query(`DROP TYPE "public"."sso_service_provider_type_enum"`); + } +} diff --git a/packages/backend/src/models/SingleSignOnServiceProvider.ts b/packages/backend/src/models/SingleSignOnServiceProvider.ts index c08db01cb..03a095754 100644 --- a/packages/backend/src/models/SingleSignOnServiceProvider.ts +++ b/packages/backend/src/models/SingleSignOnServiceProvider.ts @@ -39,6 +39,12 @@ export class MiSingleSignOnServiceProvider { }) public audience: string[]; + @Column('enum', { + enum: ['post', 'redirect'], + nullable: false, + }) + public binding: 'post' | 'redirect'; + @Column('varchar', { length: 512, }) diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts index cfdda2231..52ae6e326 100644 --- a/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/create.ts @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'write:admin:indie-auth', res: { diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts index 681884af7..96f796b6e 100644 --- a/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/delete.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'write:admin:indie-auth', errors: { diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts index 7f92577e9..32057e85f 100644 --- a/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/list.ts @@ -7,7 +7,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'read:admin:indie-auth', res: { diff --git a/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts b/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts index 5a913a8a6..e2e80a9ce 100644 --- a/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/indie-auth/update.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'write:admin:indie-auth', errors: { diff --git a/packages/backend/src/server/api/endpoints/admin/sso/create.ts b/packages/backend/src/server/api/endpoints/admin/sso/create.ts index 780fc59e7..6e7847db1 100644 --- a/packages/backend/src/server/api/endpoints/admin/sso/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/sso/create.ts @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'write:admin:sso', errors: { @@ -53,6 +53,11 @@ export const meta = { optional: false, nullable: false, items: { type: 'string', nullable: false }, }, + binding: { + type: 'string', + optional: false, nullable: false, + enum: ['post', 'redirect'], + }, acsUrl: { type: 'string', optional: false, nullable: false, @@ -88,6 +93,7 @@ export const paramDef = { type: { type: 'string', enum: ['saml', 'jwt'], nullable: false }, issuer: { type: 'string', nullable: false }, audience: { type: 'array', items: { type: 'string', nullable: false }, default: [] }, + binding: { type: 'string', enum: ['post', 'redirect'], nullable: false }, acsUrl: { type: 'string', nullable: false }, signatureAlgorithm: { type: 'string', nullable: false }, cipherAlgorithm: { type: 'string', nullable: true }, @@ -126,6 +132,7 @@ export default class extends Endpoint { // eslint- type: ps.type, issuer: ps.issuer, audience: ps.audience?.filter(i => i.length > 0) ?? [], + binding: ps.binding, acsUrl: ps.acsUrl, publicKey: publicKey, privateKey: privateKey, @@ -147,6 +154,7 @@ export default class extends Endpoint { // eslint- type: ssoServiceProvider.type, issuer: ssoServiceProvider.issuer, audience: ssoServiceProvider.audience, + binding: ssoServiceProvider.binding, acsUrl: ssoServiceProvider.acsUrl, publicKey: ssoServiceProvider.publicKey, signatureAlgorithm: ssoServiceProvider.signatureAlgorithm, diff --git a/packages/backend/src/server/api/endpoints/admin/sso/delete.ts b/packages/backend/src/server/api/endpoints/admin/sso/delete.ts index dc95e717a..2234ebb3a 100644 --- a/packages/backend/src/server/api/endpoints/admin/sso/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/sso/delete.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'write:admin:sso', errors: { diff --git a/packages/backend/src/server/api/endpoints/admin/sso/list.ts b/packages/backend/src/server/api/endpoints/admin/sso/list.ts index e68cfdb73..9adc9bc54 100644 --- a/packages/backend/src/server/api/endpoints/admin/sso/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/sso/list.ts @@ -7,7 +7,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'read:admin:sso', res: { @@ -44,6 +44,11 @@ export const meta = { optional: false, nullable: false, items: { type: 'string', nullable: false }, }, + binding: { + type: 'string', + optional: false, nullable: false, + enum: ['post', 'redirect'], + }, acsUrl: { type: 'string', optional: false, nullable: false, @@ -103,6 +108,7 @@ export default class extends Endpoint { // eslint- type: service.type, issuer: service.issuer, audience: service.audience, + binding: service.binding, acsUrl: service.acsUrl, useCertificate: service.privateKey != null, publicKey: service.publicKey, diff --git a/packages/backend/src/server/api/endpoints/admin/sso/update.ts b/packages/backend/src/server/api/endpoints/admin/sso/update.ts index c72709f5e..d0d4d153b 100644 --- a/packages/backend/src/server/api/endpoints/admin/sso/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/sso/update.ts @@ -10,7 +10,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, kind: 'write:admin:sso', errors: { @@ -29,6 +29,7 @@ export const paramDef = { name: { type: 'string', nullable: true }, issuer: { type: 'string', nullable: false }, audience: { type: 'array', items: { type: 'string', nullable: false } }, + binding: { type: 'string', enum: ['post', 'redirect'], nullable: false }, acsUrl: { type: 'string', nullable: false }, signatureAlgorithm: { type: 'string', nullable: false }, cipherAlgorithm: { type: 'string', nullable: true }, @@ -65,6 +66,7 @@ export default class extends Endpoint { // eslint- name: ps.name !== '' ? ps.name : null, issuer: ps.issuer, audience: ps.audience?.filter(i => i.length > 0), + binding: ps.binding, acsUrl: ps.acsUrl, publicKey: publicKey, privateKey: privateKey, diff --git a/packages/backend/src/server/sso/JWTIdentifyProviderService.ts b/packages/backend/src/server/sso/JWTIdentifyProviderService.ts index 36b8e811e..c639bec91 100644 --- a/packages/backend/src/server/sso/JWTIdentifyProviderService.ts +++ b/packages/backend/src/server/sso/JWTIdentifyProviderService.ts @@ -104,16 +104,10 @@ export class JWTIdentifyProviderService { }); fastify.post<{ - Body: { transaction_id: string; login_token: string; cancel?: string }; + Body: { transaction_id: string; login_token: string; }; }>('/authorize', async (request, reply) => { const transactionId = request.body.transaction_id; const token = request.body.login_token; - const cancel = !!request.body.cancel; - - if (cancel) { - reply.redirect('/'); - return; - } const transaction = await this.redisClient.get(`sso:jwt:transaction:${transactionId}`); if (!transaction) { @@ -190,13 +184,14 @@ export class JWTIdentifyProviderService { roles: roles.filter(r => r.isPublic).map(r => r.id), }; + let jwt: string; try { if (ssoServiceProvider.cipherAlgorithm) { const key = ssoServiceProvider.publicKey.startsWith('{') ? await jose.importJWK(JSON.parse(ssoServiceProvider.publicKey)) : jose.base64url.decode(ssoServiceProvider.publicKey); - const jwt = await new jose.EncryptJWT(payload) + jwt = await new jose.EncryptJWT(payload) .setProtectedHeader({ alg: ssoServiceProvider.signatureAlgorithm, enc: ssoServiceProvider.cipherAlgorithm, @@ -208,31 +203,12 @@ export class JWTIdentifyProviderService { .setJti(randomUUID()) .setSubject(user.id) .encrypt(key); - - this.#logger.info(`Redirecting to "${ssoServiceProvider.acsUrl}"`, { - userId: user.id, - ssoServiceProvider: ssoServiceProvider.id, - acsUrl: ssoServiceProvider.acsUrl, - returnTo, - }); - - if (returnTo) { - reply.redirect( - `${ssoServiceProvider.acsUrl}?jwt=${jwt}&return_to=${returnTo}`, - ); - return; - } else { - reply.redirect( - `${ssoServiceProvider.acsUrl}?jwt=${jwt}`, - ); - return; - } } else { const key = ssoServiceProvider.privateKey ? await jose.importJWK(JSON.parse(ssoServiceProvider.privateKey)) : jose.base64url.decode(ssoServiceProvider.publicKey); - const jwt = await new jose.SignJWT(payload) + jwt = await new jose.SignJWT(payload) .setProtectedHeader({ alg: ssoServiceProvider.signatureAlgorithm }) .setIssuer(ssoServiceProvider.issuer) .setAudience(ssoServiceProvider.audience) @@ -241,25 +217,6 @@ export class JWTIdentifyProviderService { .setJti(randomUUID()) .setSubject(user.id) .sign(key); - - this.#logger.info(`Redirecting to "${ssoServiceProvider.acsUrl}"`, { - userId: user.id, - ssoServiceProvider: ssoServiceProvider.id, - acsUrl: ssoServiceProvider.acsUrl, - returnTo, - }); - - if (returnTo) { - reply.redirect( - `${ssoServiceProvider.acsUrl}?jwt=${jwt}&return_to=${returnTo}`, - ); - return; - } else { - reply.redirect( - `${ssoServiceProvider.acsUrl}?jwt=${jwt}`, - ); - return; - } } } catch (err) { this.#logger.error('Failed to create JWT', { error: err }); @@ -289,6 +246,30 @@ export class JWTIdentifyProviderService { } finally { await this.redisClient.del(`sso:jwt:transaction:${transactionId}`); } + + this.#logger.info(`User "${user.username}" authorized for "${ssoServiceProvider.name ?? ssoServiceProvider.issuer}"`); + reply.header('Cache-Control', 'no-store'); + switch (ssoServiceProvider.binding) { + case 'post': return reply + .status(200) + .send({ + binding: 'post', + action: ssoServiceProvider.acsUrl, + context: { + jwt, + return_to: returnTo ?? undefined, + }, + }); + + case 'redirect': return reply + .status(200) + .send({ + binding: 'redirect', + action: !returnTo + ? `${ssoServiceProvider.acsUrl}?jwt=${jwt}` + : `${ssoServiceProvider.acsUrl}?jwt=${jwt}&return_to=${returnTo}`, + }); + } }); } diff --git a/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts b/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts index fee6e9f74..688127b27 100644 --- a/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts +++ b/packages/backend/src/server/sso/SAMLIdentifyProviderService.ts @@ -81,7 +81,7 @@ export class SAMLIdentifyProviderService { const nodes = { 'md:EntityDescriptor': { '@xmlns:md': 'urn:oasis:names:tc:SAML:2.0:metadata', - '@entityID': provider.issuer, + '@entityID': this.config.url, '@validUntil': tenYearsLater, 'md:IDPSSODescriptor': { '@WantAuthnRequestsSigned': provider.wantAuthnRequestsSigned, @@ -105,6 +105,10 @@ export class SAMLIdentifyProviderService { '@Binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', '@Location': `${this.config.url}/sso/saml/${provider.id}`, }, + { + '@Binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + '@Location': `${this.config.url}/sso/saml/${provider.id}`, + }, ], }, }, @@ -185,13 +189,14 @@ export class SAMLIdentifyProviderService { 'md:NameIDFormat': { '#text': 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', }, - 'md:AssertionConsumerService': [ - { - '@index': 1, - '@Binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', - '@Location': provider.acsUrl, - }, - ], + 'md:AssertionConsumerService': { + '@isDefault': 'true', + '@index': 0, + '@Binding': provider.binding === 'post' + ? 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' + : 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', + '@Location': provider.acsUrl, + }, }, }, }; @@ -235,7 +240,7 @@ export class SAMLIdentifyProviderService { Body?: { SAMLRequest?: string; RelayState?: string }; }>('/:serviceId', async (request, reply) => { const serviceId = request.params.serviceId; - const binding = 'redirect'; // 今はリダイレクトのみ対応 request.query?.SAMLRequest ? 'redirect' : 'post'; + const binding = request.query?.SAMLRequest ? 'redirect' : 'post'; const samlRequest = request.query?.SAMLRequest ?? request.body?.SAMLRequest; const relayState = request.query?.RelayState ?? request.body?.RelayState; @@ -284,7 +289,6 @@ export class SAMLIdentifyProviderService { `sso:saml:transaction:${transactionId}`, JSON.stringify({ serviceId: serviceId, - binding: binding, flowResult: parsed, relayState: relayState, }), @@ -350,16 +354,10 @@ export class SAMLIdentifyProviderService { ); fastify.post<{ - Body: { transaction_id: string; login_token: string; cancel?: string }; + Body: { transaction_id: string; login_token: string; }; }>('/authorize', async (request, reply) => { const transactionId = request.body.transaction_id; const token = request.body.login_token; - const cancel = !!request.body.cancel; - - if (cancel) { - reply.redirect('/'); - return; - } const transaction = await this.redisClient.get(`sso:saml:transaction:${transactionId}`); if (!transaction) { @@ -374,7 +372,7 @@ export class SAMLIdentifyProviderService { return; } - const { serviceId, binding, flowResult, relayState } = JSON.parse(transaction); + const { serviceId, flowResult, relayState } = JSON.parse(transaction); const ssoServiceProvider = await this.singleSignOnServiceProviderRepository.findOneBy({ id: serviceId, type: 'saml' }); @@ -439,7 +437,7 @@ export class SAMLIdentifyProviderService { const loginResponse = await idp.createLoginResponse( sp, flowResult, - binding, + ssoServiceProvider.binding, {}, () => { const id = idp.entitySetting.generateID?.() ?? randomUUID(); @@ -655,16 +653,27 @@ export class SAMLIdentifyProviderService { relayState, ); - this.#logger.info(`Redirecting to "${ssoServiceProvider.acsUrl}"`, { - userId: user.id, - ssoServiceProvider: ssoServiceProvider.id, - acsUrl: ssoServiceProvider.acsUrl, - relayState: relayState, - }); - + this.#logger.info(`User "${user.username}" authorized for "${ssoServiceProvider.name ?? ssoServiceProvider.issuer}"`); reply.header('Cache-Control', 'no-store'); - reply.redirect(loginResponse.context); - return; + switch (ssoServiceProvider.binding) { + case 'post': return reply + .status(200) + .send({ + binding: 'post', + action: ssoServiceProvider.acsUrl, + context: { + SAMLResponse: loginResponse.context, + RelayState: relayState ?? undefined, + }, + }); + + case 'redirect': return reply + .status(200) + .send({ + binding: 'redirect', + action: loginResponse.context, + }); + } } catch (err) { this.#logger.error('Failed to create SAML response', { error: err }); const traceableError = err as Error & { code?: string }; diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index c0be7f91b..2944930f6 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -187,7 +187,7 @@ SPDX-License-Identifier: AGPL-3.0-only - + @@ -197,6 +197,10 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + @@ -426,6 +430,7 @@ function ssoServiceAddNew() { type: 'jwt', issuer: '', audience: '', + binding: 'post', acsUrl: '', useCertificate: false, publicKey: '', @@ -457,6 +462,7 @@ async function ssoServiceSave(service) { type: service.type, issuer: service.issuer, audience: service.audience.split('\n'), + binding: service.binding, acsUrl: service.acsUrl, secret: service.publicKey, signatureAlgorithm: service.signatureAlgorithm, diff --git a/packages/frontend/src/pages/sso.vue b/packages/frontend/src/pages/sso.vue index 49a9c783d..d47688bd9 100644 --- a/packages/frontend/src/pages/sso.vue +++ b/packages/frontend/src/pages/sso.vue @@ -7,15 +7,22 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
{{ i18n.tsx._auth.shareAccess({ name }) }}
{{ i18n.ts._auth.shareAccessAsk }}
-
- - - {{ i18n.ts.cancel }} - {{ i18n.ts.accept }} -
+
+ {{ i18n.ts.cancel }} + {{ i18n.ts.accept }} +
+
+
+
{{ i18n.ts._auth.callback }}
+ +
+
+ +
+

{{ i18n.ts._auth.pleaseLogin }}

@@ -26,24 +33,63 @@ SPDX-License-Identifier: AGPL-3.0-only