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()
- Initialization:
- Declares API version
11
(compatible with newer DCR APIs)
Why This Script Is Important
- Prevents weak or plaintext secrets from being stored
- Ensures
oxTrust
andoxAuth
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
-
Upload the Script
- Go to
oxTrust > Configuration > Custom Scripts > Client Registration
- Enable the script
- Go to
-
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
- Set the list of URIs in the script property:
-
Save & Apply Changes
- If needed, restart
oxauth
or reload scripts from Admin UI
- If needed, restart
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 ""