@nodefony/security 10.0.0-beta.2 → 10.0.0-beta.3
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/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/index.js +44 -10
- package/dist/nodefony/command/security-oauth-doctor.js +66 -0
- package/dist/nodefony/command/security-secrets.js +42 -25
- package/dist/nodefony/command/security-token.js +46 -15
- package/dist/nodefony/command/security-user-delete.js +5 -4
- package/dist/nodefony/command/security-user-password.js +4 -11
- package/dist/nodefony/config/config.js +80 -5
- package/dist/nodefony/contracts/index.js +1 -2
- package/dist/nodefony/errors/InsecureTransportError.js +24 -0
- package/dist/nodefony/errors/index.js +2 -1
- package/dist/nodefony/keycloak/keycloakAdmin.js +255 -0
- package/dist/nodefony/keycloak/keycloakRealm.js +260 -0
- package/dist/nodefony/keycloak/keycloakRealmInput.js +124 -0
- package/dist/nodefony/keycloak/scaffold.js +231 -0
- package/dist/nodefony/keycloak/security-keycloak-realm.js +128 -0
- package/dist/nodefony/scaffold/contribute.js +20 -0
- package/dist/nodefony/service/authFlow.js +111 -11
- package/dist/nodefony/service/firewall.js +111 -21
- package/dist/nodefony/service/keycloak.js +209 -0
- package/dist/nodefony/service/oauth2.js +379 -26
- package/dist/nodefony/service/tokenService.js +57 -6
- package/dist/nodefony/service/totp.js +51 -6
- package/dist/nodefony/service/webAuthn.js +20 -2
- package/dist/nodefony/src/SecuredArea.js +14 -3
- package/dist/nodefony/src/admin/SecurityAdminApi.js +70 -40
- package/dist/nodefony/src/admin/adminAudit.js +0 -1
- package/dist/nodefony/src/admin/userRevocationCascade.js +79 -15
- package/dist/nodefony/src/authenticator/ExternalJwtAuthenticator.js +66 -6
- package/dist/nodefony/src/authenticator/FirewallRealtimeAuthenticator.js +38 -15
- package/dist/nodefony/src/authenticator/JwtAuthenticator.js +1 -0
- package/dist/nodefony/src/authenticator/UserPasswordAuthenticator.js +13 -3
- package/dist/nodefony/src/authenticator/authenticatorRegistry.js +9 -2
- package/dist/nodefony/src/authenticator/bearer.js +1 -2
- package/dist/nodefony/src/boot/publicRoutesNotice.js +27 -0
- package/dist/nodefony/src/entryPoint.js +39 -0
- package/dist/nodefony/src/oauth/clientCredentials.js +40 -0
- package/dist/nodefony/src/oauth/metadata.js +36 -9
- package/dist/nodefony/src/oauth/oauth2Client.js +60 -33
- package/dist/nodefony/src/oauth/oauthProviderRegistry.js +39 -6
- package/dist/nodefony/src/oauth/providerDiagnosis.js +283 -0
- package/dist/nodefony/src/oauth/providerRoles.js +79 -0
- package/dist/nodefony/src/oauth/providers/github.js +32 -6
- package/dist/nodefony/src/oauth/providers/oidc.js +82 -3
- package/dist/nodefony/src/openToSignup.js +47 -0
- package/dist/nodefony/src/sessionAuthentication.js +26 -0
- package/dist/nodefony/src/token/JwtKeystore.js +78 -46
- package/dist/nodefony/src/token/RemoteJwtVerifier.js +40 -13
- package/dist/nodefony/src/token/secretFile.js +59 -2
- package/dist/nodefony/src/transport/CredentialTransportPolicy.js +51 -0
- package/dist/types/index.d.ts +26 -6
- package/dist/types/nodefony/command/security-oauth-doctor.d.ts +23 -0
- package/dist/types/nodefony/command/security-secrets.d.ts +2 -0
- package/dist/types/nodefony/command/security-token.d.ts +9 -0
- package/dist/types/nodefony/command/security-user-delete.d.ts +4 -3
- package/dist/types/nodefony/command/security-user-password.d.ts +4 -4
- package/dist/types/nodefony/config/config.d.ts +63 -1
- package/dist/types/nodefony/contracts/IAuthenticator.d.ts +1 -1
- package/dist/types/nodefony/contracts/IFirewallDescription.d.ts +42 -1
- package/dist/types/nodefony/contracts/IOAuthProvider.d.ts +48 -0
- package/dist/types/nodefony/contracts/ISecuredArea.d.ts +22 -0
- package/dist/types/nodefony/contracts/ITotpSecret.d.ts +4 -1
- package/dist/types/nodefony/contracts/ITotpSecretStore.d.ts +7 -1
- package/dist/types/nodefony/contracts/IWebAuthnCredential.d.ts +4 -1
- package/dist/types/nodefony/contracts/IWebAuthnCredentialStore.d.ts +6 -0
- package/dist/types/nodefony/errors/InsecureTransportError.d.ts +20 -0
- package/dist/types/nodefony/errors/index.d.ts +1 -0
- package/dist/types/nodefony/keycloak/keycloakAdmin.d.ts +154 -0
- package/dist/types/nodefony/keycloak/keycloakRealm.d.ts +138 -0
- package/dist/types/nodefony/keycloak/keycloakRealmInput.d.ts +92 -0
- package/dist/types/nodefony/keycloak/scaffold.d.ts +98 -0
- package/dist/types/nodefony/keycloak/security-keycloak-realm.d.ts +43 -0
- package/dist/types/nodefony/scaffold/contribute.d.ts +15 -0
- package/dist/types/nodefony/service/authFlow.d.ts +35 -3
- package/dist/types/nodefony/service/firewall.d.ts +17 -3
- package/dist/types/nodefony/service/keycloak.d.ts +115 -0
- package/dist/types/nodefony/service/oauth2.d.ts +125 -9
- package/dist/types/nodefony/service/tokenService.d.ts +7 -1
- package/dist/types/nodefony/service/totp.d.ts +28 -4
- package/dist/types/nodefony/service/webAuthn.d.ts +23 -1
- package/dist/types/nodefony/src/SecuredArea.d.ts +4 -0
- package/dist/types/nodefony/src/admin/userRevocationCascade.d.ts +54 -12
- package/dist/types/nodefony/src/authenticator/ExternalJwtAuthenticator.d.ts +18 -0
- package/dist/types/nodefony/src/authenticator/FirewallRealtimeAuthenticator.d.ts +13 -0
- package/dist/types/nodefony/src/authenticator/UserPasswordAuthenticator.d.ts +11 -3
- package/dist/types/nodefony/src/boot/publicRoutesNotice.d.ts +16 -0
- package/dist/types/nodefony/src/entryPoint.d.ts +37 -0
- package/dist/types/nodefony/src/oauth/clientCredentials.d.ts +54 -0
- package/dist/types/nodefony/src/oauth/metadata.d.ts +24 -1
- package/dist/types/nodefony/src/oauth/oauth2Client.d.ts +50 -1
- package/dist/types/nodefony/src/oauth/oauthProviderRegistry.d.ts +46 -2
- package/dist/types/nodefony/src/oauth/providerDiagnosis.d.ts +117 -0
- package/dist/types/nodefony/src/oauth/providerRoles.d.ts +58 -0
- package/dist/types/nodefony/src/oauth/providers/oidc.d.ts +34 -1
- package/dist/types/nodefony/src/openToSignup.d.ts +36 -0
- package/dist/types/nodefony/src/sessionAuthentication.d.ts +14 -0
- package/dist/types/nodefony/src/token/JwtKeystore.d.ts +47 -5
- package/dist/types/nodefony/src/token/RemoteJwtVerifier.d.ts +29 -2
- package/dist/types/nodefony/src/token/secretFile.d.ts +22 -0
- package/dist/types/nodefony/src/transport/CredentialTransportPolicy.d.ts +47 -0
- package/docs/api-keys.md +17 -17
- package/docs/audit.md +16 -16
- package/docs/authenticators.md +60 -27
- package/docs/authorization.md +16 -16
- package/docs/cors.md +29 -29
- package/docs/csrf.md +35 -33
- package/docs/external-jwt.md +66 -4
- package/docs/firewall.md +57 -24
- package/docs/headers.md +30 -30
- package/docs/index.md +12 -4
- package/docs/keycloak.md +818 -0
- package/docs/login-page.md +361 -0
- package/docs/oauth2.md +236 -122
- package/docs/obtenir-un-jeton.md +18 -11
- package/docs/tokens.md +136 -85
- package/docs/totp.md +105 -64
- package/docs/webauthn.md +90 -87
- package/docs/webhooks.md +3 -3
- package/keycloak/themes/nodefony/account/resources/css/nodefony.css +66 -0
- package/keycloak/themes/nodefony/account/resources/img/favicon.ico +0 -0
- package/keycloak/themes/nodefony/account/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/account/theme.properties +14 -0
- package/keycloak/themes/nodefony/admin/resources/css/nodefony.css +66 -0
- package/keycloak/themes/nodefony/admin/resources/img/favicon.ico +0 -0
- package/keycloak/themes/nodefony/admin/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/admin/theme.properties +12 -0
- package/keycloak/themes/nodefony/email/html/template.ftl +73 -0
- package/keycloak/themes/nodefony/email/messages/messages_en.properties +2 -0
- package/keycloak/themes/nodefony/email/messages/messages_fr.properties +3 -0
- package/keycloak/themes/nodefony/email/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/email/theme.properties +8 -0
- package/keycloak/themes/nodefony/login/messages/messages_en.properties +22 -0
- package/keycloak/themes/nodefony/login/messages/messages_fr.properties +25 -0
- package/keycloak/themes/nodefony/login/resources/css/login.css +1141 -0
- package/keycloak/themes/nodefony/login/resources/css/nodefony.css +688 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/blueprint.css +70 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/console.css +57 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/dots.css +67 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/enterprise.css +84 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/horizon.css +158 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/ledger.css +224 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/minimal.css +63 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/photo-card.css +72 -0
- package/keycloak/themes/nodefony/login/resources/img/favicon.ico +0 -0
- package/keycloak/themes/nodefony/login/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/login/template.ftl +423 -0
- package/keycloak/themes/nodefony/login/theme.properties +17 -0
- package/keycloak/themes/nodefony/welcome/resources/css/nodefony.css +32 -0
- package/keycloak/themes/nodefony/welcome/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/welcome/theme.properties +11 -0
- package/keycloak/themes/nodefony-blueprint/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-console/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-dots/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-enterprise/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-horizon/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-ledger/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-minimal/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-photo-card/login/theme.properties +12 -0
- package/package.json +17 -15
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Thème de connexion Nodefony — l'écran Keycloak, jumeau de la page /login.
|
|
3
|
+
*
|
|
4
|
+
* Mise en page, panneau de marque (voile, courbe, arcs du logo) et TOUTES les
|
|
5
|
+
* couleurs viennent de `css/login.css`, copie conforme de `nodefony/login.css`
|
|
6
|
+
* (un test l'exige à l'octet). Cette feuille-ci ne fait que deux choses :
|
|
7
|
+
* 1. habiller les composants PatternFly que Keycloak génère (champs,
|
|
8
|
+
* boutons, cases) avec les variables `--nf-login-*` ;
|
|
9
|
+
* 2. mettre en forme le CONTENU propre à Keycloak (domaine, capacités du
|
|
10
|
+
* compte, retour à l'application).
|
|
11
|
+
*
|
|
12
|
+
* Une application change les couleurs par un thème enfant (`parent=nodefony`)
|
|
13
|
+
* qui ajoute sa feuille après celle-ci, et la mise en page par `nfLayout`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* Alias locaux : les règles PatternFly ci-dessous lisent `--nf-*`, la palette
|
|
17
|
+
vit dans `--nf-login-*`. Une seule source, des noms courts. */
|
|
18
|
+
.nf-kc {
|
|
19
|
+
--nf-heading: var(--nf-login-heading);
|
|
20
|
+
--nf-text: var(--nf-login-text);
|
|
21
|
+
--nf-muted: var(--nf-login-muted);
|
|
22
|
+
--nf-border: var(--nf-login-line-strong);
|
|
23
|
+
--nf-input: var(--nf-login-field);
|
|
24
|
+
--nf-secondary: var(--nf-login-surface);
|
|
25
|
+
--nf-secondary-hover: var(--nf-login-raised);
|
|
26
|
+
--nf-primary: var(--nf-login-brand);
|
|
27
|
+
--nf-primary-hover: var(--nf-login-brand-hover);
|
|
28
|
+
--nf-focus: var(--nf-login-accent);
|
|
29
|
+
--nf-radius: var(--nf-login-radius);
|
|
30
|
+
|
|
31
|
+
/* PatternFly reprend la police et les couleurs de la marque. Un LIEN est du
|
|
32
|
+
texte sur le fond, pas un aplat sous du blanc : il prend l'accent, lisible
|
|
33
|
+
dans les deux thèmes (le bleu de marque tombe à 2,70:1 sur fond sombre). */
|
|
34
|
+
--pf-v5-global--FontFamily--text: var(--nf-login-font);
|
|
35
|
+
--pf-v5-global--FontFamily--heading: var(--nf-login-font);
|
|
36
|
+
--pf-v5-global--link--Color: var(--nf-login-accent);
|
|
37
|
+
--pf-v5-global--link--Color--hover: var(--nf-login-heading);
|
|
38
|
+
--pf-v5-global--primary-color--100: var(--nf-login-brand);
|
|
39
|
+
--pf-v5-global--primary-color--200: var(--nf-login-brand-hover);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Le parent pose un fond « polygones » sur le body, ÉTIRÉ (`cover`) : il n'a
|
|
43
|
+
rien à faire ici. Les quatre propriétés se reposent, pas seulement l'image —
|
|
44
|
+
sans la taille, le motif d'un habillage (grille, points : une tuile de
|
|
45
|
+
quelques dizaines de pixels) était étiré à tout l'écran. */
|
|
46
|
+
.login-pf body.nf-kc {
|
|
47
|
+
background-color: var(--nf-login-bg);
|
|
48
|
+
background-image: var(--nf-login-backdrop);
|
|
49
|
+
background-size: var(--nf-login-backdrop-size);
|
|
50
|
+
background-position: var(--nf-login-backdrop-position);
|
|
51
|
+
background-repeat: repeat;
|
|
52
|
+
background-attachment: fixed;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ── Contenu du panneau de marque : le domaine et ce que le compte permet ── */
|
|
56
|
+
|
|
57
|
+
.nf-kc .nf-hero-brand .nf-logo {
|
|
58
|
+
height: 30px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.nf-hero-body {
|
|
62
|
+
max-width: 470px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Préfixés par `.nf-hero` : ce sont des <p>, et la règle `.nf-hero p` de
|
|
66
|
+
login.css (plus spécifique qu'une classe seule) les écraserait. */
|
|
67
|
+
.nf-hero .nf-hero-title {
|
|
68
|
+
max-width: none;
|
|
69
|
+
margin: 0 0 16px;
|
|
70
|
+
font-size: 44px;
|
|
71
|
+
font-weight: 650;
|
|
72
|
+
line-height: 1.05;
|
|
73
|
+
letter-spacing: -0.03em;
|
|
74
|
+
color: var(--nf-login-hero-fg);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.nf-hero .nf-hero-lead {
|
|
78
|
+
margin: 0 0 32px;
|
|
79
|
+
font-size: 18px;
|
|
80
|
+
line-height: 1.5;
|
|
81
|
+
color: var(--nf-login-hero-muted);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.nf-pill {
|
|
85
|
+
display: inline-block;
|
|
86
|
+
margin-bottom: 20px;
|
|
87
|
+
padding: 5px 12px;
|
|
88
|
+
border: 1px solid var(--nf-login-line-strong);
|
|
89
|
+
border-radius: 999px;
|
|
90
|
+
font: 600 11px/1.4 var(--nf-login-mono);
|
|
91
|
+
letter-spacing: 0.06em;
|
|
92
|
+
text-transform: uppercase;
|
|
93
|
+
color: var(--nf-login-hero-muted);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.nf-hero .nf-caps-title {
|
|
97
|
+
margin: 0 0 14px;
|
|
98
|
+
font: 600 11px/1.4 var(--nf-login-mono);
|
|
99
|
+
letter-spacing: 0.06em;
|
|
100
|
+
text-transform: uppercase;
|
|
101
|
+
color: var(--nf-login-hero-muted);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.nf-caps {
|
|
105
|
+
display: grid;
|
|
106
|
+
gap: 12px;
|
|
107
|
+
margin: 0;
|
|
108
|
+
padding: 0;
|
|
109
|
+
list-style: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.nf-caps li {
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: flex-start;
|
|
115
|
+
gap: 10px;
|
|
116
|
+
font-size: 15px;
|
|
117
|
+
line-height: 1.45;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.nf-kc .nf-caps .nf-icon {
|
|
121
|
+
flex: none;
|
|
122
|
+
width: 18px;
|
|
123
|
+
height: 18px;
|
|
124
|
+
margin-top: 2px;
|
|
125
|
+
color: var(--nf-login-ok);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.nf-hero-footer {
|
|
129
|
+
display: flex;
|
|
130
|
+
align-items: center;
|
|
131
|
+
justify-content: space-between;
|
|
132
|
+
gap: 16px;
|
|
133
|
+
font: 600 11px/1.4 var(--nf-login-mono);
|
|
134
|
+
letter-spacing: 0.04em;
|
|
135
|
+
text-transform: uppercase;
|
|
136
|
+
color: var(--nf-login-hero-muted);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* La hauteur se pose ICI, pas seulement dans l'attribut `height` du gabarit :
|
|
140
|
+
la feuille de PatternFly rend `height: auto` aux images, et le logo
|
|
141
|
+
s'afficherait alors à sa taille naturelle (256 px). */
|
|
142
|
+
.nf-kc .nf-logo {
|
|
143
|
+
display: block;
|
|
144
|
+
width: auto;
|
|
145
|
+
height: 30px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.nf-kc .nf-icon {
|
|
149
|
+
width: 20px;
|
|
150
|
+
height: 20px;
|
|
151
|
+
stroke: currentColor;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* ── Scène : la colonne du formulaire ───────────────────────────────────── */
|
|
155
|
+
|
|
156
|
+
.nf-kc .nf-stage {
|
|
157
|
+
gap: 28px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.nf-kc .pf-v5-c-login {
|
|
161
|
+
min-height: auto;
|
|
162
|
+
padding: 0;
|
|
163
|
+
background: transparent;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.nf-kc .pf-v5-c-login__container {
|
|
167
|
+
grid-template-columns: min(400px, 100%);
|
|
168
|
+
grid-template-areas: "header" "main";
|
|
169
|
+
align-content: start;
|
|
170
|
+
row-gap: 0;
|
|
171
|
+
min-height: 0;
|
|
172
|
+
padding: 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* `backdrop-filter` (même à 0 px) fait de la carte le bloc conteneur de ses
|
|
176
|
+
descendants positionnés : le sélecteur de langue s'y accrochait au lieu du
|
|
177
|
+
coin de la page. Le flou de carte n'a pas de sens sur cet écran. */
|
|
178
|
+
.nf-kc .nf-card {
|
|
179
|
+
backdrop-filter: none;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Mise en page « card » : le formulaire respire dans sa carte. */
|
|
183
|
+
.nf-kc[data-layout="card"] .pf-v5-c-login__container {
|
|
184
|
+
padding: 26px 28px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Habillage `ledger` : sur la page /login, il dispose SA carte en deux
|
|
188
|
+
colonnes (titre et étapes | formulaire) et remonte le sous-titre au-dessus
|
|
189
|
+
du titre. La carte de Keycloak porte d'autres enfants : la grille y
|
|
190
|
+
écrasait le formulaire dans la colonne de 260 px. On garde ses couleurs et
|
|
191
|
+
sa mise en page nue, pas sa structure. */
|
|
192
|
+
.nf-kc[data-skin="ledger"] {
|
|
193
|
+
/* Une colonne : la largeur d'une carte ordinaire, pas celle des deux
|
|
194
|
+
volets de /login (dont le MINIMUM dépasserait ce maximum). */
|
|
195
|
+
--nf-login-card-min: 380px;
|
|
196
|
+
--nf-login-card-ratio: 34vw;
|
|
197
|
+
--nf-login-card-width: 460px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.nf-kc[data-skin="ledger"] .nf-card {
|
|
201
|
+
grid-template-columns: 100%;
|
|
202
|
+
grid-template-rows: none;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.nf-kc[data-skin="ledger"] .nf-card h1,
|
|
206
|
+
.nf-kc[data-skin="ledger"] .nf-card .nf-sub {
|
|
207
|
+
order: 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Configuration du second facteur : consignes, code QR ET formulaire. Dans la
|
|
211
|
+
colonne de 400 px d'une connexion, la page dépassait 1 300 px de haut — un
|
|
212
|
+
écran de téléphone sur un bureau. Sur grand écran, la carte s'élargit en
|
|
213
|
+
deux colonnes : consignes et code QR à gauche, formulaire à droite ; le
|
|
214
|
+
reste (alerte, pied) occupe toute la largeur. Sur téléphone, rien ne change. */
|
|
215
|
+
@media (min-width: 960px) {
|
|
216
|
+
.nf-kc[data-page-id="login-login-config-totp"] {
|
|
217
|
+
/* Largeur FIXE de deux colonnes : minimum = maximum. */
|
|
218
|
+
--nf-login-card-min: 820px;
|
|
219
|
+
--nf-login-card-width: 820px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.nf-kc[data-page-id="login-login-config-totp"] .pf-v5-c-login__container {
|
|
223
|
+
grid-template-columns: min(820px, 100%);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* Le pied sous la carte suit sa largeur. */
|
|
227
|
+
.nf-kc[data-page-id="login-login-config-totp"] .nf-panel-footer {
|
|
228
|
+
width: min(820px, 100%);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.nf-kc[data-page-id="login-login-config-totp"] .pf-v5-c-login__main-body {
|
|
232
|
+
display: grid;
|
|
233
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
234
|
+
column-gap: 40px;
|
|
235
|
+
align-items: start;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.nf-kc[data-page-id="login-login-config-totp"]
|
|
239
|
+
.pf-v5-c-login__main-body
|
|
240
|
+
> :not(#kc-totp-settings, #kc-totp-settings-form) {
|
|
241
|
+
grid-column: 1 / -1;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Téléphone : la carte garde ses bords mais rend de la place, pour que le
|
|
246
|
+
bouton de validation reste dans l'écran sans défiler. */
|
|
247
|
+
@media (max-width: 520px) {
|
|
248
|
+
.nf-kc[data-layout="card"] .pf-v5-c-login__container {
|
|
249
|
+
padding: 20px 16px;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* Logo compact et pied de périmètre SSO : seulement quand le panneau de marque
|
|
254
|
+
est absent — mises en page card et bare, et split sur petit écran. */
|
|
255
|
+
.nf-kc .pf-v5-c-login__header {
|
|
256
|
+
display: block;
|
|
257
|
+
/* PatternFly y pose 64 px de marge haute : un vide au-dessus du logo. */
|
|
258
|
+
margin: 0;
|
|
259
|
+
padding: 0 0 24px;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.nf-kc[data-layout="split"] .pf-v5-c-login__header,
|
|
263
|
+
.nf-kc[data-layout="split"] .nf-panel-footer {
|
|
264
|
+
display: none;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
@media (max-width: 899px) {
|
|
268
|
+
.nf-kc[data-layout="split"] .pf-v5-c-login__header {
|
|
269
|
+
display: block;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.nf-kc[data-layout="split"] .nf-panel-footer {
|
|
273
|
+
display: grid;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Le haut est réservé au sélecteur de langue, posé dans le coin : sans
|
|
277
|
+
cette marge il chevauchait le bouton de retour. */
|
|
278
|
+
.nf-kc .nf-stage {
|
|
279
|
+
justify-content: flex-start;
|
|
280
|
+
padding-top: 72px;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* `keycloak.v2` habille `#kc-header-wrapper` en capitales de 29 px, avec
|
|
285
|
+
`color: var(--pf-v5-global--Color--light-100) !important` — blanc sur blanc
|
|
286
|
+
en clair. Plutôt que surenchérir d'un `!important`, la variable qu'il lit
|
|
287
|
+
est redéfinie SUR l'élément ; l'identifiant donne la priorité au reste. */
|
|
288
|
+
.nf-kc #kc-header-wrapper.nf-compact-brand {
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
gap: 10px;
|
|
292
|
+
font-size: 18px;
|
|
293
|
+
font-weight: 650;
|
|
294
|
+
letter-spacing: -0.01em;
|
|
295
|
+
line-height: 1.2;
|
|
296
|
+
text-transform: none;
|
|
297
|
+
--pf-v5-global--Color--light-100: var(--nf-heading);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.nf-kc .nf-compact-brand .nf-logo {
|
|
301
|
+
height: 26px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.nf-kc .pf-v5-c-login__main {
|
|
305
|
+
background: transparent;
|
|
306
|
+
box-shadow: none;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.nf-kc .pf-v5-c-login__main-header {
|
|
310
|
+
display: block;
|
|
311
|
+
border-top: 0;
|
|
312
|
+
padding: 0 0 28px;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.nf-kc .pf-v5-c-login__main-body,
|
|
316
|
+
.nf-kc .pf-v5-c-login__main-footer {
|
|
317
|
+
padding-inline: 0;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.nf-kc #kc-page-title {
|
|
321
|
+
font-size: var(--nf-login-title-size);
|
|
322
|
+
font-weight: 650;
|
|
323
|
+
letter-spacing: -0.01em;
|
|
324
|
+
color: var(--nf-heading);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.nf-heading {
|
|
328
|
+
display: grid;
|
|
329
|
+
gap: 6px;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.nf-subtitle {
|
|
333
|
+
grid-column: 1 / -1;
|
|
334
|
+
margin: 0;
|
|
335
|
+
font-size: 14px;
|
|
336
|
+
line-height: 1.5;
|
|
337
|
+
color: var(--nf-muted);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* Badge SSO : micro-libellé à chasse fixe, filet de l'accent. */
|
|
341
|
+
.nf-eyebrow {
|
|
342
|
+
display: inline-flex;
|
|
343
|
+
align-items: center;
|
|
344
|
+
gap: 6px;
|
|
345
|
+
justify-self: start;
|
|
346
|
+
margin-bottom: 10px;
|
|
347
|
+
padding: 4px 10px 4px 8px;
|
|
348
|
+
border: 1px solid color-mix(in srgb, var(--nf-focus) 45%, transparent);
|
|
349
|
+
border-radius: 999px;
|
|
350
|
+
color: var(--nf-focus);
|
|
351
|
+
font: 600 11px/1.4 var(--nf-login-mono);
|
|
352
|
+
letter-spacing: 0.04em;
|
|
353
|
+
text-transform: uppercase;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.nf-kc .nf-eyebrow .nf-icon {
|
|
357
|
+
width: 15px;
|
|
358
|
+
height: 15px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* ── Langue : repliée dans le coin, comme un réglage, pas comme un titre ── */
|
|
362
|
+
|
|
363
|
+
/* `absolute`, pas `fixed` : il défile avec la page au lieu de flotter
|
|
364
|
+
au-dessus du formulaire sur un petit écran. */
|
|
365
|
+
.nf-kc .pf-v5-c-login__main-header-utilities {
|
|
366
|
+
position: absolute;
|
|
367
|
+
top: 20px;
|
|
368
|
+
right: 24px;
|
|
369
|
+
z-index: 1;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.nf-kc .pf-v5-c-login__main-header-utilities .pf-v5-c-form-control {
|
|
373
|
+
min-width: 132px;
|
|
374
|
+
min-height: 34px;
|
|
375
|
+
font-size: 13px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.nf-kc .pf-v5-c-login__main-header-utilities .pf-v5-c-form-control > select {
|
|
379
|
+
min-height: 32px;
|
|
380
|
+
padding-inline-start: 10px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* ── Formulaire : champs, boutons, cases ────────────────────────────────── */
|
|
384
|
+
|
|
385
|
+
.nf-kc .pf-v5-c-form {
|
|
386
|
+
gap: 18px;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.nf-kc .pf-v5-c-form__label-text {
|
|
390
|
+
font: var(--nf-login-label-font);
|
|
391
|
+
color: var(--nf-heading);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* Champs : bordure pleine et coins arrondis. */
|
|
395
|
+
.nf-kc .pf-v5-c-form-control {
|
|
396
|
+
--pf-v5-c-form-control--BackgroundColor: var(--nf-input);
|
|
397
|
+
--pf-v5-c-form-control--Color: var(--nf-heading);
|
|
398
|
+
min-height: 44px;
|
|
399
|
+
border-radius: var(--nf-radius);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.nf-kc .pf-v5-c-form-control::before {
|
|
403
|
+
border: 1px solid var(--nf-border);
|
|
404
|
+
border-radius: var(--nf-radius);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.nf-kc .pf-v5-c-form-control > input,
|
|
408
|
+
.nf-kc .pf-v5-c-form-control > select {
|
|
409
|
+
min-height: 42px;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* PatternFly trace son focus par un trait bas en ::after — la page /login, par
|
|
413
|
+
la bordure seule. Garder les deux donnait un double trait. */
|
|
414
|
+
.nf-kc .pf-v5-c-form-control::after {
|
|
415
|
+
display: none;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.nf-kc .pf-v5-c-form-control:focus-within::before {
|
|
419
|
+
border-color: var(--nf-focus);
|
|
420
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-focus) 28%, transparent);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* Le focus se voit sur le CONTRÔLE (bordure + anneau) : le contour natif de
|
|
424
|
+
l'input s'y ajoutait en un second cadre, intérieur. */
|
|
425
|
+
.nf-kc .pf-v5-c-form-control > input:focus-visible {
|
|
426
|
+
outline: none;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.nf-kc .pf-v5-c-form-control.pf-m-error::before {
|
|
430
|
+
border-color: var(--nf-login-crit);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* Champ mot de passe : le champ et le bouton « œil » forment UN contrôle.
|
|
434
|
+
C'est le GROUPE qui porte bordure, rayon, focus et erreur : bordés
|
|
435
|
+
séparément, un trait apparaissait entre les deux en sombre et pas en clair,
|
|
436
|
+
et l'anneau de focus coupait le contrôle en deux. */
|
|
437
|
+
.nf-kc .pf-v5-c-input-group:has(.pf-v5-c-button.pf-m-control) {
|
|
438
|
+
border: 1px solid var(--nf-border);
|
|
439
|
+
border-radius: var(--nf-radius);
|
|
440
|
+
background: var(--nf-input);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.nf-kc .pf-v5-c-input-group:has(.pf-v5-c-button.pf-m-control):focus-within {
|
|
444
|
+
border-color: var(--nf-focus);
|
|
445
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--nf-focus) 28%, transparent);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.nf-kc .pf-v5-c-input-group:has(.pf-m-error) {
|
|
449
|
+
border-color: var(--nf-login-crit);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.nf-kc
|
|
453
|
+
.pf-v5-c-input-group:has(.pf-v5-c-button.pf-m-control)
|
|
454
|
+
.pf-v5-c-form-control {
|
|
455
|
+
min-height: 42px;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.nf-kc
|
|
459
|
+
.pf-v5-c-input-group:has(.pf-v5-c-button.pf-m-control)
|
|
460
|
+
.pf-v5-c-form-control::before,
|
|
461
|
+
.nf-kc
|
|
462
|
+
.pf-v5-c-input-group:has(.pf-v5-c-button.pf-m-control)
|
|
463
|
+
.pf-v5-c-form-control:focus-within::before {
|
|
464
|
+
border: 0;
|
|
465
|
+
box-shadow: none;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/* La feuille de `keycloak.v2` borde l'item du bouton à gauche : c'est le trait
|
|
469
|
+
qui coupait le contrôle en deux, visible en sombre seulement. */
|
|
470
|
+
.nf-kc
|
|
471
|
+
.pf-v5-c-input-group:has(.pf-v5-c-button.pf-m-control)
|
|
472
|
+
.pf-v5-c-input-group__item {
|
|
473
|
+
border: 0;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.nf-kc .pf-v5-c-input-group .pf-v5-c-button.pf-m-control {
|
|
477
|
+
min-height: 42px;
|
|
478
|
+
border: 0;
|
|
479
|
+
border-radius: 0 var(--nf-radius) var(--nf-radius) 0;
|
|
480
|
+
background: transparent;
|
|
481
|
+
color: var(--nf-muted);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.nf-kc .pf-v5-c-input-group .pf-v5-c-button.pf-m-control:hover {
|
|
485
|
+
color: var(--nf-heading);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.nf-kc .pf-v5-c-input-group .pf-v5-c-button.pf-m-control::after {
|
|
489
|
+
border: 0;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.nf-kc #username,
|
|
493
|
+
.nf-kc #password {
|
|
494
|
+
padding-inline-start: 40px;
|
|
495
|
+
background-repeat: no-repeat;
|
|
496
|
+
background-position: 12px center;
|
|
497
|
+
background-size: 18px 18px;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.nf-kc #username {
|
|
501
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a9099' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0'/%3E%3Cpath d='M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2'/%3E%3C/svg%3E");
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.nf-kc #password {
|
|
505
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a9099' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6'/%3E%3Cpath d='M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0'/%3E%3Cpath d='M8 11v-4a4 4 0 1 1 8 0v4'/%3E%3C/svg%3E");
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* Boutons : plein bleu pour l'action, bordé pour les secondaires. */
|
|
509
|
+
.nf-kc .pf-v5-c-button.pf-m-primary {
|
|
510
|
+
min-height: 44px;
|
|
511
|
+
border-radius: var(--nf-radius);
|
|
512
|
+
background: var(--nf-primary);
|
|
513
|
+
color: var(--nf-login-on-brand);
|
|
514
|
+
font-size: 15px;
|
|
515
|
+
font-weight: 600;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.nf-kc .pf-v5-c-button.pf-m-primary:hover,
|
|
519
|
+
.nf-kc .pf-v5-c-button.pf-m-primary:focus-visible {
|
|
520
|
+
background: var(--nf-primary-hover);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.nf-kc .pf-v5-c-button.pf-m-secondary {
|
|
524
|
+
min-height: 44px;
|
|
525
|
+
border-radius: var(--nf-radius);
|
|
526
|
+
background: var(--nf-secondary);
|
|
527
|
+
color: var(--nf-heading);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.nf-kc .pf-v5-c-button.pf-m-secondary::after {
|
|
531
|
+
border: 1px solid var(--nf-border);
|
|
532
|
+
border-radius: var(--nf-radius);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.nf-kc .pf-v5-c-button.pf-m-secondary:hover {
|
|
536
|
+
background: var(--nf-secondary-hover);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.nf-kc .pf-v5-c-button:focus-visible {
|
|
540
|
+
outline: 2px solid var(--nf-focus);
|
|
541
|
+
outline-offset: 2px;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/* Bouton principal : la flèche de la page /login (« Connexion → »). */
|
|
545
|
+
.nf-kc #kc-login {
|
|
546
|
+
display: inline-flex;
|
|
547
|
+
align-items: center;
|
|
548
|
+
justify-content: center;
|
|
549
|
+
gap: 8px;
|
|
550
|
+
margin-top: 6px;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* PatternFly dessine la bordure des boutons dans `::after`, en position
|
|
554
|
+
absolue : la flèche doit rendre la position au flux, sinon elle se colle
|
|
555
|
+
au coin haut-gauche. */
|
|
556
|
+
.nf-kc #kc-login::after {
|
|
557
|
+
content: "";
|
|
558
|
+
position: static;
|
|
559
|
+
inset: auto;
|
|
560
|
+
border: 0;
|
|
561
|
+
flex: none;
|
|
562
|
+
width: 18px;
|
|
563
|
+
height: 18px;
|
|
564
|
+
background: currentColor;
|
|
565
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l14 0'/%3E%3Cpath d='M13 18l6 -6'/%3E%3Cpath d='M13 6l6 6'/%3E%3C/svg%3E")
|
|
566
|
+
center / contain no-repeat;
|
|
567
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l14 0'/%3E%3Cpath d='M13 18l6 -6'/%3E%3Cpath d='M13 6l6 6'/%3E%3C/svg%3E")
|
|
568
|
+
center / contain no-repeat;
|
|
569
|
+
transition: transform 0.15s ease;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.nf-kc #kc-login:hover::after {
|
|
573
|
+
transform: translateX(3px);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/* « Se souvenir de moi » : case à la couleur de la marque. */
|
|
577
|
+
.nf-kc .pf-v5-c-check__input {
|
|
578
|
+
accent-color: var(--nf-primary);
|
|
579
|
+
width: 20px;
|
|
580
|
+
height: 20px;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.nf-kc .pf-v5-c-check__label {
|
|
584
|
+
font-size: 14px;
|
|
585
|
+
color: var(--nf-text);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/* ── Retour à l'application qui a demandé la connexion ──────────────────── */
|
|
589
|
+
|
|
590
|
+
.nf-back-form {
|
|
591
|
+
width: min(400px, 100%);
|
|
592
|
+
margin: 0;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.nf-back {
|
|
596
|
+
display: flex;
|
|
597
|
+
width: 100%;
|
|
598
|
+
font: inherit;
|
|
599
|
+
text-align: start;
|
|
600
|
+
cursor: pointer;
|
|
601
|
+
background: transparent;
|
|
602
|
+
align-items: center;
|
|
603
|
+
gap: 12px;
|
|
604
|
+
padding: 10px 12px;
|
|
605
|
+
border: 1px solid var(--nf-login-line);
|
|
606
|
+
border-radius: var(--nf-radius);
|
|
607
|
+
color: var(--nf-text);
|
|
608
|
+
text-decoration: none;
|
|
609
|
+
transition:
|
|
610
|
+
background-color 0.15s ease,
|
|
611
|
+
border-color 0.15s ease;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.nf-back:hover,
|
|
615
|
+
.nf-back:focus-visible {
|
|
616
|
+
border-color: var(--nf-focus);
|
|
617
|
+
background: var(--nf-secondary-hover);
|
|
618
|
+
color: var(--nf-heading);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.nf-back:focus-visible {
|
|
622
|
+
outline: 2px solid var(--nf-focus);
|
|
623
|
+
outline-offset: 2px;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.nf-kc .nf-back .nf-icon {
|
|
627
|
+
flex: none;
|
|
628
|
+
width: 18px;
|
|
629
|
+
height: 18px;
|
|
630
|
+
transition: transform 0.15s ease;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.nf-back:hover .nf-icon {
|
|
634
|
+
transform: translateX(-3px);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.nf-back strong {
|
|
638
|
+
display: block;
|
|
639
|
+
font-size: 14px;
|
|
640
|
+
font-weight: 600;
|
|
641
|
+
color: var(--nf-heading);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
.nf-back small {
|
|
645
|
+
display: block;
|
|
646
|
+
font-size: 13px;
|
|
647
|
+
color: var(--nf-muted);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/* ── Pied du panneau : le périmètre SSO, quand le panneau de marque manque ── */
|
|
651
|
+
|
|
652
|
+
.nf-panel-footer {
|
|
653
|
+
display: grid;
|
|
654
|
+
gap: 10px;
|
|
655
|
+
width: min(400px, 100%);
|
|
656
|
+
padding-top: 20px;
|
|
657
|
+
border-top: 1px solid var(--nf-login-line);
|
|
658
|
+
font-size: 13px;
|
|
659
|
+
color: var(--nf-muted);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.nf-panel-footer > span {
|
|
663
|
+
display: inline-flex;
|
|
664
|
+
align-items: center;
|
|
665
|
+
gap: 8px;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.nf-kc .nf-panel-footer .nf-icon {
|
|
669
|
+
width: 16px;
|
|
670
|
+
height: 16px;
|
|
671
|
+
flex: none;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.nf-kc .nf-dot {
|
|
675
|
+
width: 7px;
|
|
676
|
+
height: 7px;
|
|
677
|
+
margin-inline: 4px;
|
|
678
|
+
border-radius: 50%;
|
|
679
|
+
background: var(--nf-login-ok);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
@media (prefers-reduced-motion: reduce) {
|
|
683
|
+
.nf-kc #kc-login::after,
|
|
684
|
+
.nf-back,
|
|
685
|
+
.nf-kc .nf-back .nf-icon {
|
|
686
|
+
transition: none;
|
|
687
|
+
}
|
|
688
|
+
}
|