Articles in this section
Category / Section

DCR with strong client secret

Published:
5 mins read

Dynamic Client Registration (DCR) in Gluu with strong ClientSecret

What is Dynamic Client Registration?

Dynamic Client Registration (DCR) is part of the OAuth 2.0 and OpenID Connect specifications. It allows client applications to register themselves programmatically with an Authorization Server (like Gluu), without requiring prior manual configuration by an administrator.

Benefits of DCR:

  • Automated onboarding of new clients
  • Runtime configuration of client metadata (e.g., redirect URIs, scopes)
  • Ideal for microservices, CI/CD pipelines, and multi-tenant systems

In Gluu, DCR behavior is defined via custom scripts written in Python/Jython, allowing full control over how client registration is handled.


What This Script Does

This DCR script provides logic to:

✅ Scope Injection Based on Redirect URI

  • Checks if a client’s redirect URI matches any entry in a predefined list (client_redirect_uris)
  • If matched, it automatically adds the address scope to the client

🔐 Secure Secret Generation and Encryption

  • Generates a strong 32-character client secret using uppercase, lowercase, digits, and special characters
  • Encrypts the secret using Gluu’s internal StringEncrypter before storing it
  • Ensures compatibility with Gluu’s decryption mechanisms (avoiding BadPaddingException errors)

🧩 Modular and Extensible Design

  • Supports:
    • Initialization: init()
    • Cleanup: destroy()
    • Registration logic: createClient()
    • Redirect URI parsing: prepareClientRedirectUris()
  • Declares API version 11 (compatible with newer DCR APIs)

Why This Script Is Important

  • Prevents weak or plaintext secrets from being stored
  • Ensures oxTrust and oxAuth can decrypt secrets safely
  • Grants scopes conditionally based on known/trusted redirect URIs
  • Helps enforce Zero Trust registration policies
  • Aligns Gluu with RFC 7591 and RFC 7592

How to Use

  1. Upload the Script

    • Go to oxTrust > Configuration > Custom Scripts > Client Registration
    • Enable the script
  2. Configure Redirect URIs

    • Set the list of URIs in the script property:
      client_redirect_uris=https://app1.example.com/callback,https://app2.example.com/callback
      
  3. Save & Apply Changes

    • If needed, restart oxauth or reload scripts from Admin UI

Example Output

Once enabled, new dynamically registered clients will:

  • Be assigned a strong, encrypted client_secret
  • Receive additional scopes (like address) if their redirect URI matches predefined trusted values

Script

# oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
# Copyright (c) 2016, Gluu
#
# Author: Yuriy Movchan
# Modified by: Mohib Zico

from org.gluu.model.custom.script.type.client import ClientRegistrationType
from org.gluu.service.cdi.util import CdiUtil
from org.gluu.oxauth.service import ScopeService
from org.gluu.util import StringHelper, ArrayHelper
from java.util import Arrays, HashSet
from java.security import SecureRandom
from org.gluu.util.security import StringEncrypter

class ClientRegistration(ClientRegistrationType):
   def __init__(self, currentTimeMillis):
       self.currentTimeMillis = currentTimeMillis

   def init(self, customScript, configurationAttributes):
       print "Client registration. Initialization"
       self.clientRedirectUrisSet = self.prepareClientRedirectUris(configurationAttributes)
       print "Client registration. Initialized successfully"
       return True

   def destroy(self, configurationAttributes):
       print "Client registration. Destroyed successfully"
       return True

   def createClient(self, registerRequest, client, configurationAttributes):
       print "Client registration. CreateClient method"

       redirectUris = client.getRedirectUris()
       print "Client registration. Redirect URIs: %s" % redirectUris

       addAddressScope = False
       for redirectUri in redirectUris:
           if self.clientRedirectUrisSet.contains(redirectUri):
               addAddressScope = True
               break

       print "Client registration. Is add address scope: %s" % addAddressScope

       if addAddressScope:
           currentScopes = client.getScopes()
           print "Client registration. Current scopes: %s" % currentScopes

           scopeService = CdiUtil.bean(ScopeService)
           addressScope = scopeService.getScopeById("address")
           if addressScope is not None:
               newScopes = ArrayHelper.addItemToStringArray(currentScopes, addressScope.getDn())
               print "Client registration. Result scopes: %s" % newScopes
               client.setScopes(newScopes)
           else:
               print "Client registration. WARNING: 'address' scope not found by oxId"

       # Generate strong client secret
       strongSecret = self.generateStrongPassword(32)
       print "Client registration. Generated strong client_secret: %s" % strongSecret

       # Encrypt client secret
       try:
           stringEncrypter = CdiUtil.bean(StringEncrypter)
           encryptedSecret = stringEncrypter.encrypt(strongSecret)
           client.setClientSecret(encryptedSecret)
           print "Client registration. Encrypted client_secret successfully"
       except Exception as e:
           print "Client registration. ERROR: Failed to encrypt client_secret - %s" % str(e)
           return False

       return True

   def updateClient(self, registerRequest, client, configurationAttributes):
       print "Client registration. UpdateClient method"
       return True

   def getApiVersion(self):
       return 11

   def prepareClientRedirectUris(self, configurationAttributes):
       clientRedirectUrisSet = HashSet()
       if not configurationAttributes.containsKey("client_redirect_uris"):
           return clientRedirectUrisSet

       clientRedirectUrisList = configurationAttributes.get("client_redirect_uris").getValue2()
       if StringHelper.isEmpty(clientRedirectUrisList):
           print "Client registration. The property client_redirect_uris is empty"
           return clientRedirectUrisSet

       clientRedirectUrisArray = StringHelper.split(clientRedirectUrisList, ",")
       if ArrayHelper.isEmpty(clientRedirectUrisArray):
           print "Client registration. No clients specified in client_redirect_uris property"
           return clientRedirectUrisSet

       for uri in clientRedirectUrisArray:
           clientRedirectUrisSet.add(uri)

       return clientRedirectUrisSet

   def generateStrongPassword(self, length):
       charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+"
       secureRandom = SecureRandom()
       password = []

       for i in range(length):
           index = secureRandom.nextInt(len(charset))
           password.append(charset[index])

       return ''.join(password)

   def getSoftwareStatementHmacSecret(self, context):
       return ""

   def getSoftwareStatementJwks(self, context):
       return "" 
Was this article useful?
Like
Dislike
Help us improve this page
Please provide feedback or comments
Access denied
Access denied