Skip to content

OAuth2AuthorizationRequest support for some non-standard oauth provider #7714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
okhowang opened this issue Dec 10, 2019 · 18 comments
Closed
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)

Comments

@okhowang
Copy link

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

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 10, 2019
@jgrandja
Copy link
Contributor

@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.

@jgrandja jgrandja added in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) and removed status: waiting-for-triage An issue we've not yet triaged labels Dec 10, 2019
@jgrandja jgrandja self-assigned this Dec 10, 2019
@okhowang
Copy link
Author

OAuth2AuthorizationRequestResolver has many common logic, for example pkcs and so on.
i just want use OAuth2AuthorizationRequestResolver and customizer OAuth2AuthorizationRequest.Builder

@jgrandja
Copy link
Contributor

@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?

@okhowang
Copy link
Author

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

@jgrandja
Copy link
Contributor

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?

@okhowang
Copy link
Author

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()
    }

@jgrandja
Copy link
Contributor

@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.

@okhowang
Copy link
Author

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

@jgrandja
Copy link
Contributor

jgrandja commented Dec 17, 2019

@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.

@okhowang
Copy link
Author

#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");
});

@jgrandja
Copy link
Contributor

@okhowang
Copy link
Author

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

@jgrandja
Copy link
Contributor

ok so #wechat_redirect is appended at the end of URI. Let me see what I can come up with to support this.

@jgrandja
Copy link
Contributor

@okhowang Take a look at this test. This should fulfill your requirement. Let me know what you think.

@okhowang
Copy link
Author

LGTM

@qmmm61
Copy link

qmmm61 commented Oct 6, 2020

请问你是如何在authorizationRequestCustomizer中区分标准oauth2 provider与非标准的oauth2 provider?

@okhowang
Copy link
Author

okhowang commented Oct 9, 2020

请问你是如何在authorizationRequestCustomizer中区分标准oauth2 provider与非标准的oauth2 provider?

这个是在OAuth2AuthorizationRequest构造之前就要在业务逻辑里自己确定,然后调用不同的Customizer处理

@NotFound403
Copy link

NotFound403 commented Feb 23, 2022

@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();
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)
Projects
None yet
Development

No branches or pull requests

5 participants