@@ -79,10 +79,39 @@ protected function definition() {
7979 $ mform ->setDefault ('clientauthmethod ' , AUTH_OIDC_AUTH_METHOD_SECRET );
8080 $ mform ->addElement ('static ' , 'clientauthmethod_help ' , '' , get_string ('clientauthmethod_help ' , 'auth_oidc ' ));
8181
82- // Secret.
83- $ mform ->addElement ('text ' , 'clientsecret ' , auth_oidc_config_name_in_form ('clientsecret ' ), ['size ' => 60 ]);
82+ // Secret - Check if there's an existing secret to determine if we should add a "change" checkbox.
83+ $ hasexistingsecret = isset ($ this ->_customdata ['oidcconfig ' ]->clientsecret ) &&
84+ !empty ($ this ->_customdata ['oidcconfig ' ]->clientsecret );
85+
86+ if ($ hasexistingsecret ) {
87+ $ mform ->addElement (
88+ 'advcheckbox ' ,
89+ 'changesecret ' ,
90+ get_string ('change_client_secret ' , 'auth_oidc ' ),
91+ get_string ('change_client_secret_desc ' , 'auth_oidc ' )
92+ );
93+ $ mform ->setType ('changesecret ' , PARAM_BOOL );
94+ $ mform ->disabledIf ('changesecret ' , 'clientauthmethod ' , 'eq ' , AUTH_OIDC_AUTH_METHOD_CERTIFICATE );
95+
96+ // Store the original masked value in a hidden field AND as a data attribute for JavaScript to use.
97+ $ maskedsecret = auth_oidc_mask_secret ($ this ->_customdata ['oidcconfig ' ]->clientsecret );
98+ $ mform ->addElement ('hidden ' , 'originalsecretmasked ' , $ maskedsecret );
99+ $ mform ->setType ('originalsecretmasked ' , PARAM_TEXT );
100+ }
101+
102+ $ attributes = ['size ' => 60 , 'autocomplete ' => 'off ' , 'class ' => 'secret-field ' ];
103+ if ($ hasexistingsecret ) {
104+ $ maskedsecret = auth_oidc_mask_secret ($ this ->_customdata ['oidcconfig ' ]->clientsecret );
105+ $ attributes ['data-original-masked ' ] = $ maskedsecret ;
106+ }
107+ $ mform ->addElement ('text ' , 'clientsecret ' , auth_oidc_config_name_in_form ('clientsecret ' ), $ attributes );
84108 $ mform ->setType ('clientsecret ' , PARAM_TEXT );
85109 $ mform ->disabledIf ('clientsecret ' , 'clientauthmethod ' , 'neq ' , AUTH_OIDC_AUTH_METHOD_SECRET );
110+
111+ if ($ hasexistingsecret ) {
112+ $ mform ->disabledIf ('clientsecret ' , 'changesecret ' , 'notchecked ' );
113+ }
114+
86115 $ mform ->addElement ('static ' , 'clientsecret_help ' , '' , get_string ('clientsecret_help ' , 'auth_oidc ' ));
87116
88117 // Certificate source.
@@ -132,10 +161,38 @@ protected function definition() {
132161 $ mform ->disabledIf ('clientcertfile ' , 'clientcertsource ' , 'neq ' , AUTH_OIDC_AUTH_CERT_SOURCE_FILE );
133162 $ mform ->addElement ('static ' , 'clientcertfile_help ' , '' , get_string ('clientcertfile_help ' , 'auth_oidc ' ));
134163
135- // Certificate file passphrase.
136- $ mform ->addElement ('text ' , 'clientcertpassphrase ' , auth_oidc_config_name_in_form ('clientcertpassphrase ' ), ['size ' => 60 ]);
164+ // Certificate file passphrase - Check if there's an existing passphrase.
165+ $ hasexistingpassphrase = isset ($ this ->_customdata ['oidcconfig ' ]->clientcertpassphrase ) &&
166+ !empty ($ this ->_customdata ['oidcconfig ' ]->clientcertpassphrase );
167+
168+ if ($ hasexistingpassphrase ) {
169+ $ mform ->addElement (
170+ 'advcheckbox ' ,
171+ 'changecertpassphrase ' ,
172+ get_string ('change_cert_passphrase ' , 'auth_oidc ' ),
173+ get_string ('change_cert_passphrase_desc ' , 'auth_oidc ' )
174+ );
175+ $ mform ->setType ('changecertpassphrase ' , PARAM_BOOL );
176+
177+ // Store the original masked value in a hidden field AND as a data attribute for JavaScript to use.
178+ $ maskedpassphrase = auth_oidc_mask_secret ($ this ->_customdata ['oidcconfig ' ]->clientcertpassphrase );
179+ $ mform ->addElement ('hidden ' , 'originalpassphrasemasked ' , $ maskedpassphrase );
180+ $ mform ->setType ('originalpassphrasemasked ' , PARAM_TEXT );
181+ }
182+
183+ $ attributes = ['size ' => 60 , 'autocomplete ' => 'off ' , 'class ' => 'secret-field ' ];
184+ if ($ hasexistingpassphrase ) {
185+ $ maskedpassphrase = auth_oidc_mask_secret ($ this ->_customdata ['oidcconfig ' ]->clientcertpassphrase );
186+ $ attributes ['data-original-masked ' ] = $ maskedpassphrase ;
187+ }
188+ $ mform ->addElement ('text ' , 'clientcertpassphrase ' , auth_oidc_config_name_in_form ('clientcertpassphrase ' ), $ attributes );
137189 $ mform ->setType ('clientcertpassphrase ' , PARAM_TEXT );
138190 $ mform ->disabledIf ('clientcertpassphrase ' , 'clientauthmethod ' , 'neq ' , AUTH_OIDC_AUTH_METHOD_CERTIFICATE );
191+
192+ if ($ hasexistingpassphrase ) {
193+ $ mform ->disabledIf ('clientcertpassphrase ' , 'changecertpassphrase ' , 'notchecked ' );
194+ }
195+
139196 $ mform ->addElement ('static ' , 'clientcertpassphrase_help ' , '' , get_string ('clientcertpassphrase_help ' , 'auth_oidc ' ));
140197
141198 // Endpoints header.
@@ -172,6 +229,11 @@ protected function definition() {
172229 $ mform ->setDefault ('oidcscope ' , 'openid profile email ' );
173230 $ mform ->addElement ('static ' , 'oidcscope_help ' , '' , get_string ('oidcscope_help ' , 'auth_oidc ' ));
174231
232+ // Custom Claim.
233+ $ mform ->addElement ('text ' , 'customclaims ' , auth_oidc_config_name_in_form ('customclaims ' ), ['size ' => 120 ]);
234+ $ mform ->setType ('customclaims ' , PARAM_TEXT );
235+ $ mform ->setDefault ('customclaims ' , '' );
236+ $ mform ->addElement ('static ' , 'customclaims_help ' , '' , get_string ('customclaims_help ' , 'auth_oidc ' ));
175237 // Secret expiry notifications recipients.
176238 if (auth_oidc_is_local_365_installed ()) {
177239 $ mform ->addElement (
@@ -230,7 +292,21 @@ public function validation($data, $files) {
230292 // Validate authentication variables.
231293 switch ($ data ['clientauthmethod ' ]) {
232294 case AUTH_OIDC_AUTH_METHOD_SECRET :
233- if (empty (trim ($ data ['clientsecret ' ]))) {
295+ // Check if user is attempting to change the secret.
296+ $ changesecret = isset ($ data ['changesecret ' ]) ? $ data ['changesecret ' ] : true ;
297+ $ existingsecret = get_config ('auth_oidc ' , 'clientsecret ' );
298+
299+ if ($ changesecret ) {
300+ // User wants to change the secret, validate the new value.
301+ if (empty (trim ($ data ['clientsecret ' ]))) {
302+ $ errors ['clientsecret ' ] = get_string ('error_empty_client_secret ' , 'auth_oidc ' );
303+ } else if (auth_oidc_is_masked_secret ($ data ['clientsecret ' ])) {
304+ // User checked "change secret" but didn't enter a new value.
305+ $ errors ['clientsecret ' ] = get_string ('error_masked_secret_not_changed ' , 'auth_oidc ' );
306+ }
307+ } else if (empty ($ existingsecret ) && empty (trim ($ data ['clientsecret ' ]))) {
308+ // No existing secret and field is empty - this is invalid.
309+ // This handles edge cases where checkbox logic fails.
234310 $ errors ['clientsecret ' ] = get_string ('error_empty_client_secret ' , 'auth_oidc ' );
235311 }
236312 break ;
@@ -253,6 +329,16 @@ public function validation($data, $files) {
253329 }
254330 break ;
255331 }
332+
333+ // Validate certificate passphrase if user is attempting to change it.
334+ $ changecertpassphrase = isset ($ data ['changecertpassphrase ' ]) ? $ data ['changecertpassphrase ' ] : true ;
335+
336+ if ($ changecertpassphrase && !empty ($ data ['clientcertpassphrase ' ])) {
337+ if (auth_oidc_is_masked_secret ($ data ['clientcertpassphrase ' ])) {
338+ // User checked "change passphrase" but didn't enter a new value.
339+ $ errors ['clientcertpassphrase ' ] = get_string ('error_masked_secret_not_changed ' , 'auth_oidc ' );
340+ }
341+ }
256342 break ;
257343 }
258344
@@ -299,6 +385,70 @@ public function validation($data, $files) {
299385 }
300386 }
301387
388+ // Validate custom claims.
389+ if (!empty ($ data ['customclaims ' ])) {
390+ $ claims = explode (' ' , $ data ['customclaims ' ]);
391+ foreach ($ claims as $ claim ) {
392+ $ claim = trim ($ claim );
393+ if (!empty ($ claim ) && !preg_match ('/^[a-zA-Z0-9_-]+$/ ' , $ claim )) {
394+ $ errors ['customclaims ' ] = get_string ('error_invalid_custom_claim ' , 'auth_oidc ' );
395+ break ;
396+ }
397+ }
398+ }
399+
302400 return $ errors ;
303401 }
402+
403+ /**
404+ * Process data after form definition and data loading.
405+ * This is called after set_data() and after validation errors, allowing us to override submitted values.
406+ *
407+ * @return void
408+ */
409+ public function definition_after_data () {
410+ parent ::definition_after_data ();
411+
412+ $ mform =& $ this ->_form ;
413+
414+ // Get the current checkbox states using getSubmitValue (works for submitted data).
415+ $ changesecret = $ mform ->getSubmitValue ('changesecret ' );
416+ $ changecertpassphrase = $ mform ->getSubmitValue ('changecertpassphrase ' );
417+
418+ // If the "change secret" checkbox is NOT checked and there's an existing secret,
419+ // ensure the field shows the masked value (especially important after validation errors).
420+ if (isset ($ this ->_customdata ['oidcconfig ' ]->clientsecret ) && !empty ($ this ->_customdata ['oidcconfig ' ]->clientsecret )) {
421+ $ currentvalue = $ mform ->getSubmitValue ('clientsecret ' );
422+
423+ // If checkbox is not checked and field is empty or doesn't match masked value, restore it.
424+ if (empty ($ changesecret ) && (empty ($ currentvalue ) || !auth_oidc_is_masked_secret ($ currentvalue ))) {
425+ $ maskedsecret = auth_oidc_mask_secret ($ this ->_customdata ['oidcconfig ' ]->clientsecret );
426+ // Force the element to use the masked value.
427+ $ element = $ mform ->getElement ('clientsecret ' );
428+ $ element ->setValue ($ maskedsecret );
429+
430+ // Also update the data attribute for JavaScript reliability.
431+ $ element ->updateAttributes (['data-original-masked ' => $ maskedsecret ]);
432+ }
433+ }
434+
435+ // Same logic for certificate passphrase.
436+ if (
437+ isset ($ this ->_customdata ['oidcconfig ' ]->clientcertpassphrase ) &&
438+ !empty ($ this ->_customdata ['oidcconfig ' ]->clientcertpassphrase )
439+ ) {
440+ $ currentvalue = $ mform ->getSubmitValue ('clientcertpassphrase ' );
441+
442+ // If checkbox is not checked and field is empty or doesn't match masked value, restore it.
443+ if (empty ($ changecertpassphrase ) && (empty ($ currentvalue ) || !auth_oidc_is_masked_secret ($ currentvalue ))) {
444+ $ maskedpassphrase = auth_oidc_mask_secret ($ this ->_customdata ['oidcconfig ' ]->clientcertpassphrase );
445+ // Force the element to use the masked value.
446+ $ element = $ mform ->getElement ('clientcertpassphrase ' );
447+ $ element ->setValue ($ maskedpassphrase );
448+
449+ // Also update the data attribute for JavaScript reliability.
450+ $ element ->updateAttributes (['data-original-masked ' => $ maskedpassphrase ]);
451+ }
452+ }
453+ }
304454}
0 commit comments