@@ -276,6 +276,11 @@ type OAuthClientSecretsUpdateInstructionGenerateData struct {
276276 ClientID string `json:"clientID,omitempty"`
277277}
278278
279+ type OAuthClientSecretsUpdateInstructionDeleteData struct {
280+ ClientID string `json:"clientID,omitempty"`
281+ KeyID string `json:"keyID,omitempty"`
282+ }
283+
279284type OAuthClientSecretsUpdateInstructionCleanupData struct {
280285 KeepClientIDs []string `json:"keepClientIDs,omitempty"`
281286}
@@ -284,13 +289,16 @@ type OAuthClientSecretsUpdateInstruction struct {
284289 Action SecretUpdateInstructionAction `json:"action,omitempty"`
285290
286291 GenerateData * OAuthClientSecretsUpdateInstructionGenerateData `json:"generateData,omitempty"`
292+ DeleteData * OAuthClientSecretsUpdateInstructionDeleteData `json:"deleteData,omitempty"`
287293 CleanupData * OAuthClientSecretsUpdateInstructionCleanupData `json:"cleanupData,omitempty"`
288294}
289295
290296func (i * OAuthClientSecretsUpdateInstruction ) ApplyTo (ctx * SecretConfigUpdateInstructionContext , currentConfig * SecretConfig ) (* SecretConfig , error ) {
291297 switch i .Action {
292298 case SecretUpdateInstructionActionGenerate :
293299 return i .generate (ctx , currentConfig )
300+ case SecretUpdateInstructionActionDelete :
301+ return i .delete (currentConfig )
294302 case SecretUpdateInstructionActionCleanup :
295303 return i .cleanup (currentConfig )
296304 default :
@@ -320,31 +328,37 @@ func (i *OAuthClientSecretsUpdateInstruction) generate(ctx *SecretConfigUpdateIn
320328
321329 clientID := i .GenerateData .ClientID
322330 jwkKey := ctx .GenerateClientSecretOctetKeyFunc (ctx .Clock .NowUTC (), corerand .SecureRand )
323- keySet := jwk .NewSet ()
324- _ = keySet .AddKey (jwkKey )
325- newCredentialsItem := OAuthClientCredentialsItem {
326- ClientID : clientID ,
327- OAuthClientCredentialsKeySet : OAuthClientCredentialsKeySet {Set : keySet },
328- }
329331
330332 newOAuthClientCredentials := & OAuthClientCredentials {}
331333 idx , item , found := out .Lookup (OAuthClientCredentialsKey )
334+ // If the secret exist, reuse existing item
332335 if found {
333336 oauth , err := i .decodeOAuthClientCredentials (item .RawData )
334337 if err != nil {
335338 return nil , err
336339 }
337- _ , ok := oauth .Lookup (clientID )
338- if ok {
339- return nil , fmt .Errorf ("config: client secret already exist" )
340+ newOAuthClientCredentials = oauth
341+ }
342+
343+ // Find the existing client item by index
344+ var clientItem * OAuthClientCredentialsItem
345+ if existingClientItem , ok := newOAuthClientCredentials .Lookup (clientID ); ok {
346+ clientItem = existingClientItem
347+ } else {
348+ newClientItem := OAuthClientCredentialsItem {
349+ ClientID : clientID ,
350+ OAuthClientCredentialsKeySet : OAuthClientCredentialsKeySet {Set : jwk .NewSet ()},
340351 }
341- // copy oauth client secret items from the current config to new config
342- newOAuthClientCredentials .Items = make ([]OAuthClientCredentialsItem , len (oauth .Items ))
343- copy (newOAuthClientCredentials .Items , oauth .Items )
352+ newOAuthClientCredentials .Items = append (newOAuthClientCredentials .Items , newClientItem )
353+ clientItem = & newClientItem
354+ }
355+
356+ // Append the new key
357+ if clientItem .OAuthClientCredentialsKeySet .Len () >= 2 {
358+ return nil , fmt .Errorf ("config: must have at most two OAuth client secrets for client %s" , clientID )
344359 }
360+ _ = clientItem .OAuthClientCredentialsKeySet .AddKey (jwkKey )
345361
346- // Add new credentials item to the OAuthClientCredentials
347- newOAuthClientCredentials .Items = append (newOAuthClientCredentials .Items , newCredentialsItem )
348362 var jsonData []byte
349363 jsonData , err := json .Marshal (newOAuthClientCredentials )
350364 if err != nil {
@@ -364,6 +378,75 @@ func (i *OAuthClientSecretsUpdateInstruction) generate(ctx *SecretConfigUpdateIn
364378 return out , nil
365379}
366380
381+ func (i * OAuthClientSecretsUpdateInstruction ) delete (currentConfig * SecretConfig ) (* SecretConfig , error ) {
382+ out := & SecretConfig {}
383+ out .Secrets = make ([]SecretItem , len (currentConfig .Secrets ))
384+ copy (out .Secrets , currentConfig .Secrets )
385+
386+ if i .DeleteData == nil || i .DeleteData .ClientID == "" || i .DeleteData .KeyID == "" {
387+ return nil , fmt .Errorf ("config: missing clientID or keyID for OAuthClientSecretsUpdateInstruction" )
388+ }
389+
390+ clientID := i .DeleteData .ClientID
391+ keyID := i .DeleteData .KeyID
392+
393+ idx , item , found := out .Lookup (OAuthClientCredentialsKey )
394+ if ! found {
395+ return out , nil
396+ }
397+ oauth , err := i .decodeOAuthClientCredentials (item .RawData )
398+ if err != nil {
399+ return nil , err
400+ }
401+
402+ ctx := contextForTheUnusedContextArgumentInJWXV2API
403+ var newOAuthClientCredentialsItems []OAuthClientCredentialsItem
404+ for _ , existingItem := range oauth .Items {
405+ if existingItem .ClientID == clientID {
406+ var foundKey jwk.Key
407+ for it := existingItem .OAuthClientCredentialsKeySet .Set .Keys (ctx ); it .Next (ctx ); {
408+ key := it .Pair ().Value .(jwk.Key )
409+ if key .KeyID () == keyID {
410+ foundKey = key
411+ break
412+ }
413+ }
414+
415+ if foundKey != nil {
416+ err := existingItem .OAuthClientCredentialsKeySet .Set .RemoveKey (foundKey )
417+ if err != nil {
418+ return nil , err
419+ }
420+ }
421+
422+ // Check length
423+ if existingItem .OAuthClientCredentialsKeySet .Len () == 0 {
424+ return nil , fmt .Errorf ("config: cannot delete the last secret for client %s" , clientID )
425+ }
426+
427+ newOAuthClientCredentialsItems = append (newOAuthClientCredentialsItems , existingItem )
428+ } else {
429+ newOAuthClientCredentialsItems = append (newOAuthClientCredentialsItems , existingItem )
430+ }
431+ }
432+ newOAuthClientCredentials := & OAuthClientCredentials {
433+ Items : newOAuthClientCredentialsItems ,
434+ }
435+
436+ var jsonData []byte
437+ jsonData , err = json .Marshal (newOAuthClientCredentials )
438+ if err != nil {
439+ return nil , err
440+ }
441+ newSecretItem := SecretItem {
442+ Key : OAuthClientCredentialsKey ,
443+ RawData : json .RawMessage (jsonData ),
444+ }
445+ out .Secrets [idx ] = newSecretItem
446+
447+ return out , nil
448+ }
449+
367450func (i * OAuthClientSecretsUpdateInstruction ) cleanup (currentConfig * SecretConfig ) (* SecretConfig , error ) {
368451 out := & SecretConfig {}
369452 out .Secrets = make ([]SecretItem , len (currentConfig .Secrets ))
0 commit comments