/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
*/
package appauth
import std.collection.HashSet
import stdx.encoding.json.JsonObject
import std.collection.HashMap
import std.collection.Set
import std.collection.Map
import stdx.encoding.json.JsonValue
public class AuthorizationRequest <: AuthorizationManagementRequest{
public static let CODE_CHALLENGE_METHOD_S256: String = "S256"
public static let CODE_CHALLENGE_METHOD_PLAIN: String = "plain"
public static let PARAM_CLIENT_ID: String = "client_id"
public static let PARAM_CODE_CHALLENGE: String = "code_challenge"
public static let PARAM_CODE_CHALLENGE_METHOD: String = "code_challenge_method"
public static let PARAM_DISPLAY: String = "display"
public static let PARAM_LOGIN_HINT: String = "login_hint"
public static let PARAM_PROMPT: String = "prompt"
public static let PARAM_UI_LOCALES: String = "ui_locales"
public static let PARAM_REDIRECT_URI: String = "redirect_uri"
public static let PARAM_RESPONSE_MODE: String = "response_mode"
public static let PARAM_RESPONSE_TYPE: String = "response_type"
public static let PARAM_SCOPE: String = "scope"
public static let PARAM_STATE: String = "state"
public static let PARAM_NONCE: String = "nonce"
public static let PARAM_CLAIMS: String = "claims"
public static let PARAM_CLAIMS_LOCALES: String = "claims_locales"
private static let PARAMS_ARRAY: Array<String> = [
PARAM_CLIENT_ID,
PARAM_CODE_CHALLENGE,
PARAM_CODE_CHALLENGE_METHOD,
PARAM_DISPLAY,
PARAM_LOGIN_HINT,
PARAM_PROMPT,
PARAM_UI_LOCALES,
PARAM_REDIRECT_URI,
PARAM_RESPONSE_MODE,
PARAM_RESPONSE_TYPE,
PARAM_SCOPE,
PARAM_NONCE,
PARAM_CLAIMS,
PARAM_CLAIMS_LOCALES
]
static let BUILT_IN_PARAMS: HashSet<String> = AdditionalParamsProcessor.builtInParams(PARAMS_ARRAY)
private static let KEY_CONFIGURATION: String = "configuration"
private static let KEY_CLIENT_ID: String = "clientId"
private static let KEY_DISPLAY: String = "display"
private static let KEY_LOGIN_HINT: String = "login_hint"
private static let KEY_PROMPT: String = "prompt"
private static let KEY_UI_LOCALES: String = "ui_locales"
private static let KEY_RESPONSE_TYPE: String = "responseType"
private static let KEY_REDIRECT_URI: String = "redirectUri"
private static let KEY_SCOPE: String = "scope"
private static let KEY_STATE: String = "state"
private static let KEY_NONCE: String = "nonce"
private static let KEY_CODE_VERIFIER: String = "codeVerifier"
private static let KEY_CODE_VERIFIER_CHALLENGE: String = "codeVerifierChallenge"
private static let KEY_CODE_VERIFIER_CHALLENGE_METHOD: String = "codeVerifierChallengeMethod"
private static let KEY_RESPONSE_MODE: String = "responseMode"
private static let KEY_CLAIMS: String = "claims"
private static let KEY_CLAIMS_LOCALES: String = "claimsLocales"
private static let KEY_ADDITIONAL_PARAMETERS: String = "additionalParameters"
public let configuration: AuthorizationServiceConfiguration
public let clientId: String
public let display: String
public let loginHint: String
public let prompt: String
public let uiLocales: String
public let responseType: String
public let redirectUri: AuthUri
public let scope: String
public let state: String
public let nonce: String
public let codeVerifier: String
public let codeVerifierChallenge: String
public let codeVerifierChallengeMethod: String
public let responseMode: String
public let claims: JsonObject
public let claimsLocales: String
public let additionalParameters: Map<String, String>
init(
configuration: AuthorizationServiceConfiguration,
clientId: String,
responseType: String,
redirectUri: AuthUri,
display: String,
loginHint: String,
prompt: String,
uiLocales: String,
scope: String,
state: String,
nonce: String,
codeVerifier: String,
codeVerifierChallenge: String,
codeVerifierChallengeMethod: String,
responseMode: String,
claims: JsonObject,
claimsLocales: String,
additionalParameters: Map<String, String>
) {
// mandatory fields
this.configuration = configuration
this.clientId = clientId
this.responseType = responseType
this.redirectUri = redirectUri
this.additionalParameters = additionalParameters
// optional fields
this.display = display
this.loginHint = loginHint
this.prompt = prompt
this.uiLocales = uiLocales
this.scope = scope
this.state = state
this.nonce = nonce
this.codeVerifier = codeVerifier
this.codeVerifierChallenge = codeVerifierChallenge
this.codeVerifierChallengeMethod = codeVerifierChallengeMethod
this.responseMode = responseMode
this.claims = claims
this.claimsLocales = claimsLocales
}
/**
* 获取scope集合
* @return scope集合
*/
public func getScopeSet(): Set<String> {
return AsciiStringListUtil.stringToSet(scope)
}
/**
* 获取prompt值集合
* @return prompt值集合
*/
public func getPromptValues(): Set<String> {
return AsciiStringListUtil.stringToSet(prompt)
}
/**
* 获取UI语言环境集合
* @return UI语言环境集合
*/
public func getUiLocales(): Set<String> {
return AsciiStringListUtil.stringToSet(uiLocales)
}
/**
* 获取状态值
* @return 状态值
*/
public func getState(): String {
return state
}
/**
* 获取声明语言环境集合
* @return 声明语言环境集合
*/
public func getClaimsLocales(): Set<String> {
return AsciiStringListUtil.stringToSet(claimsLocales)
}
public override func toUri(): AuthUri {
var authorizationEndpoint = configuration.authorizationEndpoint.getOrThrow()
var builder = authorizationEndpoint.buildUpon()
.appendQueryParameter(PARAM_REDIRECT_URI, redirectUri.toString())
.appendQueryParameter(PARAM_CLIENT_ID, clientId)
.appendQueryParameter(PARAM_RESPONSE_TYPE, responseType)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_DISPLAY, display)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_LOGIN_HINT, loginHint)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_PROMPT, prompt)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_UI_LOCALES, uiLocales)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_STATE, state)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_NONCE, nonce)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_SCOPE, scope)
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_RESPONSE_MODE, responseMode)
if (!codeVerifier.isEmpty()) {
builder.appendQueryParameter(PARAM_CODE_CHALLENGE, codeVerifierChallenge)
.appendQueryParameter(PARAM_CODE_CHALLENGE_METHOD, codeVerifierChallengeMethod)
}
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_CLAIMS, claims.toString())
UriUtil.appendQueryParameterIfNotNull(builder, PARAM_CLAIMS_LOCALES, claimsLocales)
var keys = additionalParameters.keys()
for (key in keys) {
builder.appendQueryParameter(key, additionalParameters.get(key).getOrThrow())
}
return builder.build()
}
/**
* 将对象序列化为JSON对象
* @return JSON对象
*/
public override func jsonSerialize(): JsonObject {
var json: JsonObject = JsonObject()
JsonUtil.put(json, KEY_CONFIGURATION, configuration.toJson())
JsonUtil.put(json, KEY_CLIENT_ID, clientId)
JsonUtil.put(json, KEY_RESPONSE_TYPE, responseType)
JsonUtil.put(json, KEY_REDIRECT_URI, redirectUri.toString())
JsonUtil.putIfNotNull(json, KEY_DISPLAY, display)
JsonUtil.putIfNotNull(json, KEY_LOGIN_HINT, loginHint)
JsonUtil.putIfNotNull(json, KEY_SCOPE, scope)
JsonUtil.putIfNotNull(json, KEY_PROMPT, prompt)
JsonUtil.putIfNotNull(json, KEY_UI_LOCALES, uiLocales)
JsonUtil.putIfNotNull(json, KEY_STATE, state)
JsonUtil.putIfNotNull(json, KEY_NONCE, nonce)
JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER, codeVerifier)
JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER_CHALLENGE, codeVerifierChallenge)
JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER_CHALLENGE_METHOD,
codeVerifierChallengeMethod)
JsonUtil.putIfNotNull(json, KEY_RESPONSE_MODE, responseMode)
JsonUtil.putIfNotNull(json, KEY_CLAIMS, claims)
JsonUtil.putIfNotNull(json, KEY_CLAIMS_LOCALES, claimsLocales)
JsonUtil.put(json, KEY_ADDITIONAL_PARAMETERS,
JsonUtil.mapToJsonObject(additionalParameters))
return json
}
/**
* 将对象序列化为JSON字符串
* @return JSON字符串
*/
public func jsonSerializeString(): String {
return jsonSerialize().toString()
}
/**
* 从JSON对象反序列化为AuthorizationRequest对象
* @param json JSON对象
* @return AuthorizationRequest对象
*/
public static func jsonDeserialize(json: JsonObject): AuthorizationRequest {
return AuthorizationRequest(
AuthorizationServiceConfiguration.fromJson(json.get(KEY_CONFIGURATION).getOrThrow().asObject()),
JsonUtil.getString(json, KEY_CLIENT_ID),
JsonUtil.getString(json, KEY_RESPONSE_TYPE),
JsonUtil.getUri(json, KEY_REDIRECT_URI),
JsonUtil.getStringIfDefined(json, KEY_DISPLAY),
JsonUtil.getStringIfDefined(json, KEY_LOGIN_HINT),
JsonUtil.getStringIfDefined(json, KEY_PROMPT),
JsonUtil.getStringIfDefined(json, KEY_UI_LOCALES),
JsonUtil.getStringIfDefined(json, KEY_SCOPE),
JsonUtil.getStringIfDefined(json, KEY_STATE),
JsonUtil.getStringIfDefined(json, KEY_NONCE),
JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER),
JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER_CHALLENGE),
JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER_CHALLENGE_METHOD),
JsonUtil.getStringIfDefined(json, KEY_RESPONSE_MODE),
JsonUtil.getJsonObjectIfDefined(json, KEY_CLAIMS),
JsonUtil.getStringIfDefined(json, KEY_CLAIMS_LOCALES),
JsonUtil.getStringMap(json, KEY_ADDITIONAL_PARAMETERS))
}
/**
* 从JSON字符串反序列化为AuthorizationRequest对象
* @param jsonStr JSON字符串
* @return AuthorizationRequest对象
* @throws NoneValueException 当JSON字符串为空时抛出异常
*/
public static func jsonDeserialize(jsonStr: String): AuthorizationRequest {
if (jsonStr.isEmpty()) {
throw NoneValueException("json string cannot be null")
}
return jsonDeserialize(JsonValue.fromStr(jsonStr).asObject())
}
}
public class AuthorizationRequest_Builder {
private var mConfiguration: AuthorizationServiceConfiguration
private var mClientId: String = ""
private var mDisplay: String = ""
private var mLoginHint: String = ""
private var mPrompt: String = ""
private var mUiLocales: String = ""
private var mResponseType: String = ""
private var mRedirectUri: AuthUri
private var mScope: String = ""
private var mState: String = ""
private var mNonce: String = ""
private var mCodeVerifier: String = ""
private var mCodeVerifierChallenge: String = ""
private var mCodeVerifierChallengeMethod: String = ""
private var mResponseMode: String = ""
private var mClaims: JsonObject = JsonObject()
private var mClaimsLocales: String = ""
private var mAdditionalParameters: HashMap<String, String> = HashMap<String, String>()
public init(
configuration: AuthorizationServiceConfiguration,
clientId: String,
responseType: String,
redirectUri: AuthUri
) {
mConfiguration = configuration
mClientId = clientId
mRedirectUri = redirectUri
setResponseType(responseType)
setState(AuthorizationManagementUtil.generateRandomState())
setNonce(AuthorizationManagementUtil.generateRandomState())
setCodeVerifier(CodeVerifierUtil.generateRandomCodeVerifier())
}
/**
* 设置授权服务配置
* @param configuration 授权服务配置
* @return AuthorizationRequest_Builder对象
*/
public func setAuthorizationServiceConfiguration(configuration: AuthorizationServiceConfiguration): AuthorizationRequest_Builder {
mConfiguration = configuration
return this
}
/**
* 设置客户端ID
* @param clientId 客户端ID
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当客户端ID为空时抛出异常
*/
public func setClientId(clientId: String): AuthorizationRequest_Builder {
if (clientId.isEmpty()) {
throw NoneValueException("client ID cannot be empty")
} else {
mClientId = clientId
}
return this
}
/**
* 设置显示类型
* @param display 显示类型
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当显示类型为空时抛出异常
*/
public func setDisplay(display: String): AuthorizationRequest_Builder {
if (display.isEmpty()) {
throw NoneValueException("display cannot be empty")
} else {
mDisplay = display
}
return this
}
/**
* 设置登录提示
* @param loginHint 登录提示
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当登录提示为空时抛出异常
*/
public func setLoginHint(loginHint: String): AuthorizationRequest_Builder {
if (loginHint.isEmpty()) {
throw NoneValueException("loginHint cannot be empty")
} else {
mLoginHint = loginHint
}
return this
}
/**
* 设置提示类型
* @param prompt 提示类型
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当提示类型为空时抛出异常
*/
public func setPrompt(prompt: String): AuthorizationRequest_Builder {
if (prompt.isEmpty()) {
throw NoneValueException("prompt cannot be empty")
} else {
mPrompt = prompt
}
return this
}
/**
* 设置提示值数组
* @param promptValues 提示值数组
* @return AuthorizationRequest_Builder对象
*/
public func setPromptValues(promptValues: Array<String>): AuthorizationRequest_Builder {
if (promptValues.isEmpty()) {
mPrompt = ""
} else {
mPrompt = AsciiStringListUtil.iterableToString(promptValues)
}
return this
}
/**
* 设置UI语言环境
* @param uiLocales UI语言环境
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当UI语言环境为空时抛出异常
*/
public func setUiLocales(uiLocales: String): AuthorizationRequest_Builder {
if (uiLocales.isEmpty()) {
throw NoneValueException("uiLocales cannot be empty")
} else {
mUiLocales = uiLocales
}
return this
}
/**
* 设置UI语言环境值数组
* @param promptValues UI语言环境值数组
* @return AuthorizationRequest_Builder对象
*/
public func setUiLocalesValues(promptValues: Array<String>): AuthorizationRequest_Builder {
if (promptValues.isEmpty()) {
mUiLocales = ""
} else {
mUiLocales = AsciiStringListUtil.iterableToString(promptValues)
}
return this
}
/**
* 设置响应类型
* @param responseType 响应类型
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当响应类型为空时抛出异常
*/
public func setResponseType(responseType: String): AuthorizationRequest_Builder {
if (responseType.isEmpty()) {
throw NoneValueException("expected response type cannot be empty")
} else {
mResponseType = responseType
}
return this
}
/**
* 设置重定向URI
* @param redirectUri 重定向URI
* @return AuthorizationRequest_Builder对象
*/
public func setRedirectUri(redirectUri: AuthUri): AuthorizationRequest_Builder {
mRedirectUri = redirectUri
return this
}
/**
* 设置作用域
* @param scope 作用域
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当作用域为空时抛出异常
*/
public func setScope(scope: String): AuthorizationRequest_Builder {
if (scope.isEmpty()) {
throw NoneValueException("scope cannot be empty")
} else {
mScope = scope
}
return this
}
/**
* 设置作用域数组
* @param scopes 作用域数组
* @return AuthorizationRequest_Builder对象
*/
public func setScopes(scopes: Array<String>): AuthorizationRequest_Builder {
if (scopes.isEmpty()) {
mScope = ""
} else {
mScope = AsciiStringListUtil.iterableToString(scopes)
}
return this
}
/**
* 设置状态值
* @param state 状态值
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当状态值为空时抛出异常
*/
public func setState(state: String): AuthorizationRequest_Builder {
if (state.isEmpty()) {
throw NoneValueException("state cannot be empty")
} else {
mState = state
}
return this
}
/**
* 设置nonce值
* @param nonce nonce值
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当nonce值为空时抛出异常
*/
public func setNonce(nonce: String): AuthorizationRequest_Builder {
if (nonce.isEmpty()) {
throw NoneValueException("nonce cannot be empty")
} else {
mNonce = nonce
}
return this
}
/**
* 设置代码验证器
* @param codeVerifier 代码验证器
* @return AuthorizationRequest_Builder对象
*/
public func setCodeVerifier(codeVerifier: String): AuthorizationRequest_Builder {
if (!codeVerifier.isEmpty()) {
CodeVerifierUtil.checkCodeVerifier(codeVerifier)
mCodeVerifier = codeVerifier
mCodeVerifierChallenge = CodeVerifierUtil.deriveCodeVerifierChallenge(codeVerifier)
mCodeVerifierChallengeMethod = CodeVerifierUtil.getCodeVerifierChallengeMethod()
} else {
mCodeVerifier = ""
mCodeVerifierChallenge = ""
mCodeVerifierChallengeMethod = ""
}
return this
}
/**
* 设置代码验证器及相关参数
* @param codeVerifier 代码验证器
* @param codeVerifierChallenge 代码验证器挑战值
* @param codeVerifierChallengeMethod 代码验证器挑战方法
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当参数不匹配时抛出异常
*/
public func setCodeVerifier(codeVerifier: String, codeVerifierChallenge: String, codeVerifierChallengeMethod: String): AuthorizationRequest_Builder {
if (!codeVerifier.isEmpty()) {
CodeVerifierUtil.checkCodeVerifier(codeVerifier)
if (codeVerifierChallenge.isEmpty()) {
throw NoneValueException("code verifier challenge cannot be null or empty if verifier is set")
}
if (codeVerifierChallengeMethod.isEmpty()) {
throw NoneValueException("code verifier challenge method cannot be null or empty if verifier is set")
}
} else {
if (!codeVerifierChallenge.isEmpty()) {
throw NoneValueException("code verifier challenge must be null if verifier is null")
}
if (!codeVerifierChallengeMethod.isEmpty()) {
throw NoneValueException("code verifier challenge method must be null if verifier is null")
}
}
mCodeVerifier = codeVerifier
mCodeVerifierChallenge = codeVerifierChallenge
mCodeVerifierChallengeMethod = codeVerifierChallengeMethod
return this
}
/**
* 设置响应模式
* @param responseMode 响应模式
* @return AuthorizationRequest_Builder对象
* @throws NoneValueException 当响应模式为空时抛出异常
*/
public func setResponseMode(responseMode: String): AuthorizationRequest_Builder {
if (responseMode.isEmpty()) {
throw NoneValueException("responseMode must not be empty")
} else {
mResponseMode = responseMode
}
return this
}
/**
* 设置声明
* @param claims 声明
* @return AuthorizationRequest_Builder对象
*/
public func setClaims(claims: JsonObject): AuthorizationRequest_Builder {
mClaims = claims
return this
}
/**
* 设置声明语言环境值数组
* @param claimsLocalesValues 声明语言环境值数组
* @return AuthorizationRequest_Builder对象
*/
public func setClaimsLocales(claimsLocalesValues: Array<String>): AuthorizationRequest_Builder {
mClaimsLocales = AsciiStringListUtil.iterableToString(claimsLocalesValues)
return this
}
/**
* 设置附加参数
* @param additionalParameters 附加参数
* @return AuthorizationRequest_Builder对象
*/
public func setAdditionalParameters(additionalParameters: HashMap<String, String>): AuthorizationRequest_Builder {
var tmpMap = AdditionalParamsProcessor.checkAdditionalParams(additionalParameters, AuthorizationRequest.BUILT_IN_PARAMS)
mAdditionalParameters.add(all: tmpMap)
return this
}
/**
* 构建AuthorizationRequest对象
* @return AuthorizationRequest对象
*/
public func build(): AuthorizationRequest {
return AuthorizationRequest(
mConfiguration,
mClientId,
mResponseType,
mRedirectUri,
mDisplay,
mLoginHint,
mPrompt,
mUiLocales,
mScope,
mState,
mNonce,
mCodeVerifier,
mCodeVerifierChallenge,
mCodeVerifierChallengeMethod,
mResponseMode,
mClaims,
mClaimsLocales,
HashMap<String, String>(mAdditionalParameters)
)
}
}
public class AuthorizationRequest_Display {
public static let PAGE: String = "page"
public static let POPUP: String = "popup"
public static let TOUCH: String = "touch"
public static let WAP: String = "wap"
}
public class AuthorizationRequest_Prompt {
public static let NONE: String = "none"
public static let LOGIN: String = "login"
public static let CONSENT: String = "consent"
public static let SELECT_ACCOUNT: String = "select_account"
}
public class AuthorizationRequest_Scope {
public static let ADDRESS: String = "address"
public static let EMAIL: String = "email"
public static let OFFLINE_ACCESS: String = "offline_access"
public static let OPENID: String = "openid"
public static let PHONE: String = "phone"
public static let PROFILE: String = "profile"
}
public class AuthorizationRequest_ResponseMode {
public static let QUERY: String = "query"
public static let FRAGMENT: String = "fragment"
}