Summary
in china, some oauth provider's implementation is not standard enough
for example wechat, it use appid instead of client_id and must add url hash #wechat_redirect
can we add more customization for OAuth2AuthorizationRequest and OAuth2AuthorizationRequestResolver
Actual Behavior
Expected Behavior
Configuration
Version
Sample
Comment From: jgrandja
@okhowang You can customize the OAuth2AuthorizationRequest by supplying a custom OAuth2AuthorizationRequestResolver. See the reference doc for an example.
You can also customize the Token Request.
I'm going to close this issue as I feel I answered your question.
Comment From: okhowang
OAuth2AuthorizationRequestResolver has many common logic, for example pkcs and so on. i just want use OAuth2AuthorizationRequestResolver and customizer OAuth2AuthorizationRequest.Builder
Comment From: jgrandja
@okhowang I'm not sure I understand your request. Can you provide a more detailed explanation or even a code sample of what you would like to accomplish?
Comment From: okhowang
for example, when call with a standard oauth provider
spring security will redirect to url like https://some.domain.com/authorize?client_id=foo&scope=bar&response_type=code&redirect_uri=https://some.url/
but in wechat it will be https://some.domain.com/authorize?appid=foo&scope=bar&response_type=code&redirect_uri=https://some.url/#wechat_redirect
of course, i can customize OAuth2AuthorizationRequestResolver to do this.
but i also need other standard oauth provider
I want reuse spring's OAuth2AuthorizationRequestResolver class, and just customize OAuth2AuthorizationRequest.Builder's logic which determine redirect url directly.
I don't want modify any in OAuth2AuthorizationRequestResolver except OAuth2AuthorizationRequest.Builder
Comment From: jgrandja
Ok I understand now.
So let's say we add DefaultOAuth2AuthorizationRequestResolver.setAuthorizationRequestConsumer(Consumer<OAuth2AuthorizationRequest.Builder> builderConsumer). This will allow you to supply a Consumer and hook into the build process.
However, it still will not let you change the parameter names. You need to change client_id to appid but the Consumer will only allow you to override the parameter values. If you want to change the parameter names than you need to use the delegation-based strategy. Does this make sense or am I missing something?
Comment From: okhowang
final redirect uri is make by OAuth2AuthorizationRequest.Builder#buildAuthorizationRequestUri
I write a builder which use a parameterNameMap to map oauth2 original parameterName to provider's parametersName now
private fun set(map: MultiValueMap<String, String>, name: String, value: String) {
map[parameterNameMap[name] ?: name] = value
}
private fun buildAuthorizationRequestUri(): String {
val parameters = LinkedMultiValueMap<String, String>()
set(parameters, OAuth2ParameterNames.RESPONSE_TYPE, this.responseType.value)
set(parameters, OAuth2ParameterNames.CLIENT_ID, this.clientId)
scopes?.let { set(parameters, OAuth2ParameterNames.SCOPE, it.joinToString(" ")) }
state?.let { set(parameters, OAuth2ParameterNames.STATE, it) }
redirectUri?.let { set(parameters, OAuth2ParameterNames.REDIRECT_URI, it) }
additionalParameters?.let { it.forEach { (key, value) -> parameters.set(key, value.toString()) } }
return UriComponentsBuilder.fromHttpUrl(this.authorizationUri)
.queryParams(parameters)
.fragment(clientRegistration.authorizationHash()) // add custom fragment
.encode(StandardCharsets.UTF_8)
.build()
.toUriString()
}
Comment From: jgrandja
@okhowang I just submitted #7748. Please see this test, which should handle your use case. Please let me know if this will work for you.
Comment From: okhowang
In parameters, it looks like ok
but how to it for url segment, aka hash? or more?
we may not need a Consumer for OAuth2AuthorizationRequest.Builder
a interface for OAuth2AuthorizationRequest.Builder may be better?
and we can set a customize implementation of RequestBuilder for DefaultOAuth2AuthorizationRequestResolver
Comment From: jgrandja
@okhowang
but how to it for url segment, aka hash?
You can update like this:
this.resolver.setAuthorizationRequestCustomizer(customizer -> {
// TODO Append #wechat_redirect (Option 1)
customizer.redirectUri(...);
// TODO Append #wechat_redirect (Option 2)
customizer.parameters(parameters -> {
String redirectUri = (String) parameters.get(OAuth2ParameterNames.REDIRECT_URI);
redirectUri += "#wechat_redirect";
parameters.put(OAuth2ParameterNames.REDIRECT_URI, redirectUri);
});
});
a interface for OAuth2AuthorizationRequest.Builder may be better?
I'm not sure if an interface would be better. I believe a Consumer is simpler.
Do you see any limitations with the Consumer<OAuth2AuthorizationRequest.Builder> approach? If so, please provide specific details so I can understand and improve.
Comment From: okhowang
#wechat_redirect need be add on authorizationUri not redirectUri
or builder add a hash optional
like below
this.resolver.setAuthorizationRequestCustomizer(customizer -> {
customizer.hash("wechat_redirect");
});
Comment From: jgrandja
I thought you need to add to redirect_uri?
but in wechat it will be https://some.domain.com/authorize?appid=foo&scope=bar&response_type=code&redirect_uri=https://some.url/#wechat_redirect
Comment From: okhowang
I'm sorry for bad case
in wechat doc it's like https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
Comment From: jgrandja
ok so #wechat_redirect is appended at the end of URI. Let me see what I can come up with to support this.
Comment From: jgrandja
@okhowang Take a look at this test. This should fulfill your requirement. Let me know what you think.
Comment From: okhowang
LGTM
Comment From: qmmm61
请问你是如何在authorizationRequestCustomizer中区分标准oauth2 provider与非标准的oauth2 provider?
Comment From: okhowang
请问你是如何在authorizationRequestCustomizer中区分标准oauth2 provider与非标准的oauth2 provider?
这个是在OAuth2AuthorizationRequest构造之前就要在业务逻辑里自己确定,然后调用不同的Customizer处理
Comment From: NotFound403
@jgrandja i have done it,but something not good here,OAuth2AuthorizationRequest.Builder need to expose a method to get registrationId for ensure the right provider. this customizer must not executed when it is google or okta etc .
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.util.Assert;
import org.springframework.web.util.UriBuilder;
import java.net.URI;
import java.util.Map;
import java.util.function.Consumer;
/**
* customizer {@link OAuth2AuthorizationRequest}
* <p>
* client_id to appid ,add fragment wechat_redirect
* @see DefaultOAuth2AuthorizationRequestResolver#setAuthorizationRequestCustomizer(Consumer)
* @author felord.cn
*/
public class WechatOAuth2AuthorizationRequestCustomizer {
private static final String WECHAT_APP_ID = "appid";
private static final String WECHAT_FRAGMENT = "wechat_redirect";
private final String wechatRegistrationId;
public WechatOAuth2AuthorizationRequestCustomizer(String wechatRegistrationId) {
Assert.notNull(wechatRegistrationId, "wechat registrationId flag must not be null");
this.wechatRegistrationId = wechatRegistrationId;
}
public void customize(OAuth2AuthorizationRequest.Builder builder) {
//todo i need a method to get registrationId here So that the following code logic can be executed
builder.parameters(WechatOAuth2AuthorizationRequestCustomizer::wechatParametersConsumer);
builder.authorizationRequestUri(WechatOAuth2AuthorizationRequestCustomizer::authorizationRequestUriFunction);
}
private static void wechatParametersConsumer(Map<String, Object> parameters) {
// client_id replace into appid here
LinkedHashMap<String, Object> linkedParameters = new LinkedHashMap<>();
// k v must be ordered
parameters.forEach((k,v)->{
if (OAuth2ParameterNames.CLIENT_ID.equals(k)){
linkedParameters.put(WECHAT_APP_ID,v);
}else {
linkedParameters.put(k,v);
}
});
parameters.clear();
parameters.putAll(linkedParameters);
}
private static URI authorizationRequestUriFunction(UriBuilder builder) {
// add wechat fragment here
return builder.fragment(WECHAT_FRAGMENT).build();
}
}