OIDC Single Sign-On Custom Domain Replacement Steps (Deployment Plan URL Address Replacement Solution, No Longer Recommended)¶
Introduction¶
Guance Deployment Plan supports implementing single sign-on based on the OIDC protocol with custom domain address replacement, to solve workspace OIDC login access via custom domain.
!!! warning
This solution is no longer recommended for new access scenarios.
It is currently more recommended to use [Custom OIDC Access (Deployment Plan)](./oidc-custom-access.md), which unifies the login address, `redirect_uri`, callback address, and user information formatting logic in Func. This method is more suitable for handling single IDP, multiple IDPs, and various non-standard OIDC compatibility scenarios.
This document is retained for maintaining existing environments or compatibility with historical configuration scenarios.
Concepts¶
| Term | Explanation |
|---|---|
| loginUrl | Guance OIDC login entry address, generally in the format http://domain/oidc/login, or with some query parameters on the address |
| authUrl | Account authentication address |
| callbackURL | The address to callback to Guance after successful account authentication, generally in the format http://domain/oidc/callback |
| redirect_uri | The callback address parameter name carried in the authUrl |
Steps¶
1. OIDC Basic Configuration¶
1) In the Guance Launcher namespace: forethought-core > core, add the sub-configuration item requestSet to the OIDCClientSet configuration item.
# OIDC client configuration (When wellKnowURL is configured in this item, the KeyCloakPassSet configuration item automatically becomes invalid)
OIDCClientSet:
# OIDC Endpoints configuration address, i.e., the complete `https://xxx.xxx.com/xx/.well-known/openid-configuration` address.
wellKnowURL: https://xxx.xxx.com/xx/.well-known/openid-configuration
# Client ID provided by the authentication service
clientId: xxx
# Client's Secret key
clientSecret: xxxx
# The following is the custom configuration part (used to customize various addresses in the oidc process) Can be copied directly
requestSet:
login:
redirectUriFormatRequest:
# Turn on this switch
isOpen: true
url: "External function request address in func (corresponding to the redirect_uri_format function external link below)"
urlFormatRequest:
# Switch, default is off
isOpen: true
url: "External function request address in func (corresponding to the login_auth_url_format function external link below)"
callback:
redirectUriFormatRequest:
# Switch, default is off
isOpen: true
description: "Same configuration description as redirectUriFormatRequest under login"
url: "External function request address in func (corresponding to the redirect_uri_format function external link below)"
urlFormatRequest:
# Switch, default is off
isOpen: true
url: "External function request address in func (corresponding to the callback_url_format function external link below)"
Additional notes:
- For new access scenarios, it is recommended to switch to Custom OIDC Access (Deployment Plan), consolidating address formatting logic in Func for processing;
- Besides writing directly into the configuration,
OIDCClientSet.clientSecretcan also be managed securely via password-type environment variables on the Func side, avoiding writing sensitive information in plain text in scripts or example code.
1. redirect_uri_format Function Description¶
If there are non-standard redirect_uri changes in the entire OIDC flow, formatting should be performed in an external function. Ensure that the redirect_uri in the oidc client is consistent during login and callback requests, otherwise client validation of state and code will fail.
python
# Request method: post
# Request body content is as follows:
{
"type": "login", # Indicates whether the change corresponds to the login or callback flow
"redirect_uri": "Original redirect_uri address",
"args": {
# Query parameters received by the oidc/login request
},
"headers": {
# Request header data received by the oidc/login request
}
}
# Response content is as follows:
{
"redirect_uri": "Modified redirect_uri",
}
2. login_auth_url_format Function Description¶
Forward the redirect address of oidc/login to an external function for repackaging before performing the address redirect.
python
# Request method: post
# Request body content is as follows:
{
"type": "login", # This is the login type, login indicates from login, callback indicates from callback request
"url": "Original OIDC login address",
"args": {
# Query parameters received by the oidc/login request
},
"headers": {
# Request header data received by the oidc/login request
}
}
# Response content is as follows:
{
"url": "Formatted auth_url",
}
3. callback_url_format Function Description¶
Forward the redirect address of oidc/callback to an external function for repackaging before performing the address redirect.
python
# Request method: post
# Request body content is as follows:
{
"type": "callback", # This is the login type, indicating a request from the callback flow
"url": "Originally generated redirect address",
"args": {
# Query parameters received by the oidc/login request
},
"headers": {
# Request header data received by the oidc/login request
}
}
# Response content is as follows:
{
"url": "Formatted url",
}
2. Add Script in Built-in Func.¶
Note: This script can be copied directly.
!!! warning
The following script is only used to demonstrate address formatting processing logic.
It is not recommended to hardcode `clientSecret`, Tokens, or other sensitive credentials directly in the script. If secret information is needed, prioritize injection and reading via password-type environment variables on the Func side.
import json
import copy
import requests
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
from collections import OrderedDict
def parse_url(url):
'''
Parse the url and return a unified url parsing object and query parameter dictionary data
'''
# Parse url information
parsed_url = urlparse(url)
# Domain passing is placed in the url parameters
query_params = parse_qs(parsed_url.query)
# Use OrderedDict to maintain the original order of parameters
ordered_params = OrderedDict(query_params)
return parsed_url, ordered_params
def __make_redirect_uri(url, headers):
'''
Extract the request source address from the request headers, this address is extracted from the X-Forwarded-Host header.
'''
# Parse redirect_uri
parsed_url, ordered_params = parse_url(url)
x_host = headers.get("X-Forwarded-Host")
x_port = headers.get("X-Forwarded-Port")
x_scheme = headers.get("X-Forwarded-Scheme", "").lower()
if not x_scheme:
x_scheme = copy.deepcopy(parsed_url.scheme)
netloc = copy.deepcopy(parsed_url.netloc)
if x_host:
if (x_scheme == "http" and x_port == "80") or (x_scheme == "https" and x_port == "443"):
netloc = x_host
else:
netloc = f"{x_host}:{x_port}" if x_port else x_host
parsed_url = parsed_url._replace(scheme=x_scheme, netloc=netloc)
new_url = urlunparse(parsed_url)
return new_url
@DFF.API('Corresponding to redirectUriFormatRequest function - Format the redirect_uri information in the oidc client')
def redirect_uri_format(**kwargs):
'''
When interfacing with the Guance OIDC flow, if non-standard processes cause changes to the redirect_uri parameter name or value, it needs to be processed through this function.
Parameters:
type {str} The type corresponding to the current operation in the oidc flow, login indicates address changes generated by the oidc/login request; callback indicates requests generated by the oidc/callback flow
redirect_uri {str} Original redirect_uri address
args {json} Query parameters from the corresponding request in the flow
headers {json} Information from the request headers of the corresponding request in the flow
return {"redirect_uri": "Modified redirect_uri address"}
'''
print("kwargs--->>>", json.dumps(kwargs))
# Extract the original redirect_uri address and request header information
redirect_uri = kwargs.get("redirect_uri", "")
headers = kwargs.get("headers", {})
# Generate a new redirect_uri address
new_url = __make_redirect_uri(redirect_uri, headers)
result = {
# This address provides the original address for obtaining the login authentication code
"redirect_uri": new_url,
}
print("result-->>", result)
return result
@DFF.API('Corresponding to urlFormatRequest function - Format the redirect address information in login')
def login_auth_url_format(**kwargs):
'''
When interfacing with the Guance OIDC flow, if non-standard processes require changes to parameters in the login address, they can be processed in this function.
Parameters:
type {str} The type corresponding to the current operation in the oidc flow, login indicates address changes generated by the oidc/login request; callback indicates requests generated by the oidc/callback flow
url {str} Original url address
args {json} Query parameters from the corresponding request in the flow
headers {json} Information from the request headers of the corresponding request in the flow
return {"url": "Modified url address"}
'''
print("kwargs--->>>", json.dumps(kwargs))
url = kwargs.get("url")
new_url = None
args = kwargs.get("args")
headers = kwargs.get("headers")
new_host = headers.get("X-From")
if new_host:
# Parse url information
parsed_url = urlparse(url)
parsed_url = parsed_url._replace(netloc=new_host)
new_url = urlunparse(parsed_url)
result = {
# This address provides the original address for obtaining the login authentication code
"url": new_url or url
}
print("result-->>", result)
return result
@DFF.API('Corresponding to urlFormatRequest function - Format the redirect address information in callback')
def callback_url_format(**kwargs):
'''
When interfacing with the Guance OIDC flow, if non-standard processes require changes to parameters in the address for logging into the Guance workspace after callback, they can be processed in this function.
Parameters:
type {str} The type corresponding to the current operation in the oidc flow, login indicates address changes generated by the oidc/login request; callback indicates requests generated by the oidc/callback flow
url {str} Original url address
args {json} Query parameters from the corresponding request in the flow
headers {json} Information from the request headers of the corresponding request in the flow
return {"url": "Modified url address"}
'''
print("kwargs--->>>", json.dumps(kwargs))
type = kwargs.get("type")
url = kwargs.get("url")
args = kwargs.get("args") or {}
headers = kwargs.get("headers") or {}
new_url = None
from_v = args.get("from")
if from_v:
# Parse url
parsed_url, ordered_params = parse_url(url)
ordered_params["from"] = from_v
# Rebuild the query string, maintaining the original order
new_query_string = urlencode(ordered_params, doseq=True)
parsed_url = parsed_url._replace(query=new_query_string)
new_url = urlunparse(parsed_url)
result = {
# This address provides the address adjustment for logging into the frontend after successful callback
"url": new_url or url,
}
return result
Note, it is necessary to enable authorization links for the three functions redirect_uri_format, login_auth_url_format, and callback_url_format.