Skip to content
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

Using Auth0 as IDP and manage on Client the redirection. #23

Closed
AlexandreRoba opened this issue Jan 10, 2024 · 17 comments · Fixed by #24
Closed

Using Auth0 as IDP and manage on Client the redirection. #23

AlexandreRoba opened this issue Jan 10, 2024 · 17 comments · Fixed by #24

Comments

@AlexandreRoba
Copy link
Contributor

AlexandreRoba commented Jan 10, 2024

Hi @ctron , Thanks for replying to me.

Here is the issue I'm facing. I'm trying to use Auth0 as IDP and I need to configure on Auth0 the authorized returnUrls.
Auth0 does not allow to have "Generic" returnURLs. So we need to have a fixed set of defined returned Url.

But when using Yew-Oauth (Which is a great Thanks for that :p) the token lives in the app context.So on any refresh or reload of the page we loose this token and we are redirected to auth0 to get a new token. Which makes full sens. But we can be redirected from any page on the app which requires authentication such as for example https://localhost:3000/orders/xxxx/items/details...... xxx could be infinite... So no way to configure this on Auth0.

One way to tackle this could be to use on our app a defined url such as /callback?appReturnUrl=<<https://localhost:3000/orders/xxxx/items/details must be url encoded of course :p>> or a state parameter which will contains the url of the protected ressource and redirect the user to this value when /callback is called.

In order to do so we need to be able to "Inject" the returnUrl when redirecting to the authorization endpoint when accessing a protected ressource taking the current page url and add it as the appREturnURl or state.

My understanding is that we should use for this the LoginOptions parameters, so as a first step I used this code:

let login_options = LoginOptions::new()
        .with_redirect_url(Url::parse("http://localhost:3000/callback").unwrap());
    html!(
        <>
            <OAuth2 {config}
                scopes={vec!["openid".into(),"email".into(),"offline_access".into(),"api:call".into()]}
                audience={"http://localhost:8081/api"}
                options={login_options}>
                <Content/>
            </OAuth2>
        </>
    )

My understanding is that it is supposed to override the default returnUrl with what it is specified on the with_redirect_url. But it does not. When the user tries to access a protected ressources he still get as returnUrl the url of the protected ressource:

response_type: code
client_id: XXXXXXXXXXXX
state: eMhCkjz4-qTShr93Iw-qmw
code_challenge: lwo9FNEKznQ7xgho_ylMrxU_71zHzQoHlq907uQ83yY
code_challenge_method: S256
redirect_uri: http://localhost:3000/
scope: openid openid email offline_access api:call
audience: http://localhost:8081/api
nonce: B4qwU8ekDmymf9b6Mzongw

Once I have this working I will need a way on this configuration to inject the url of the protected ressource as the appReturnURL...

I do not see how to do this differently in order to implement the silent login on an unlimited custom list of return urls with auth0 as they do not support wildcard parameters on the return url :(

This is also what is explain here https://auth0.com/docs/authenticate/login/redirect-users-after-login.

@ctron
Copy link
Owner

ctron commented Jan 10, 2024

There's a LoginOptions struct which allows to supply the redirect URL:

pub options: Option<LoginOptions>,

It defaults to the current URL. I am not sure this is exposed at the moment. But I think this should be the right feature to leverage.

@ctron
Copy link
Owner

ctron commented Jan 10, 2024

Ah, double checking … it's exposed as part of the OAuth2 component.

@ctron
Copy link
Owner

ctron commented Jan 10, 2024

Ah … and triple checking (should have done that first) … that was you already use. Hm …

@ctron
Copy link
Owner

ctron commented Jan 10, 2024

Ok, checking the Auth0 docs, that looks like an interesting limitation. IIRC Keycloak does allow a prefix, and even allow for certain wildards.

So I guess you need to follow the idea of Auth0, and encode this somewhere in a cookie, session, or state variable.

@AlexandreRoba
Copy link
Contributor Author

Hi @ctron,

Yes indeed. Unless I'm mistaken but I have set LoginOptions but it does not seems to be taken into account. :( It is always sets the returnUrl as the one of the ressource.

My second problem is to be able to capture the url of the ressource that I'm trying to access in order to be able to pass it in a state or a cookie to the authorization endpoint. Is this supported? An idea on how to do this?

@ctron
Copy link
Owner

ctron commented Jan 10, 2024

Taking a closer look at the code, I think everything should already be there:

yew-oauth2/src/agent/mod.rs

Lines 441 to 450 in 62187a9

let login_context = client.make_login_context(config, redirect_url.clone())?;
SessionStorage::set(STORAGE_KEY_CSRF_TOKEN, login_context.csrf_token)
.map_err(|err| OAuth2Error::StartLogin(err.to_string()))?;
SessionStorage::set(STORAGE_KEY_LOGIN_STATE, login_context.state)
.map_err(|err| OAuth2Error::StartLogin(err.to_string()))?;
SessionStorage::set(STORAGE_KEY_REDIRECT_URL, redirect_url)
.map_err(|err| OAuth2Error::StartLogin(err.to_string()))?;

I am not sure why it doesn't work … I guess you will need to debug this.

@AlexandreRoba
Copy link
Contributor Author

AlexandreRoba commented Jan 11, 2024

Hi @ctron. I'm trying to find out what is going on. I have forked the solution and set couple of log points:
I can see the LoginOptions is used and set in the agent context using the OAuth component.
But then once I start the login process the LoginOptions is back to the default value.
Screenshot 2024-01-11 at 13 48 55

There is something clearly happening somewhere that cleans the LoginOptions because the audience and the scopes are conserved which are set at the same place are conserved:

<OAuth2 {config}
                scopes={vec!["openid".into(),"email".into(),"offline_access".into(),"api:call".into()]}
                audience={"http://localhost:8081/api"}
                options={login_options}>
                <Content/>
</OAuth2>

@ctron
Copy link
Owner

ctron commented Jan 11, 2024

Weird indeed. But I have no idea what's going on. And you seem to have a reproducer at hand :)

@AlexandreRoba
Copy link
Contributor Author

AlexandreRoba commented Jan 11, 2024

I've found this article that describe the need for anyone else reading the issue https://community.auth0.com/t/how-do-i-set-up-a-dynamic-allowed-callback-url/60268

@ctron I'm going to investigate a little further but I'm not even sure I will be able to capture and store the protected ressource url with the yew-oauth2 API before starting the login process. I'm wondering if I would not be better building my own custom oauth agent for auth0. I need to have it working now. :( thanks for the help you provided.

@AlexandreRoba
Copy link
Contributor Author

@ctron Do you know why is LoginOption passed as parameters on the start_login?

  fn start_login(&mut self, options: LoginOptions) -> Result<(), OAuth2Error> {
        let client = self.client.as_ref().ok_or(OAuth2Error::NotInitialized)?;
        let config = self.config.as_ref().ok_or(OAuth2Error::NotInitialized)?;
        log::info!("start_login config are: {:?}", self.config);
        let redirect_url = match options.redirect_url {
            Some(redirect_url) => redirect_url,
            None => Self::current_url().map_err(OAuth2Error::StartLogin)?,
        };

Cause LoginOptions is an attribute of the config and is set there. I mean InnerConfig has an attributes option which contains the proper LoginOption value....

@ctron
Copy link
Owner

ctron commented Jan 11, 2024

IIRC the original ideas was to allow one to manually start a login process (e.g. from your own component) with some additional options.

@ctron
Copy link
Owner

ctron commented Jan 11, 2024

@ctron I'm going to investigate a little further but I'm not even sure I will be able to capture and store the protected ressource url with the yew-oauth2 API before starting the login process.

In the code I linked earlier, you will find 3 variables which are stored in the session store. I would suggest to add that information there. Then check the corresponding section where those variables are read again, and I you find our value stored, apply it.

I'm wondering if I would not be better building my own custom oauth agent for auth0. I need to have it working now. :(

I you believe that to the faster, that might be your better approach then. But from what I see, it should just be a few changes. But I also can't do that for you, as I don't have your environment set up, and also don't have the time to invest into that issue right now.

@AlexandreRoba
Copy link
Contributor Author

AlexandreRoba commented Jan 11, 2024

IIRC the original ideas was to allow one to manually start a login process (e.g. from your own component) with some additional options.

That is an excellent idea! I could leverage this. Then I guess this is where it happens. It never takes the one coming from the inner config and takes the one from the "manual" start and this override the one set on the OAuth2 component. I will look into this.

@kate-shine
Copy link
Contributor

Thanks for dealing with this :) I'm facing the same issue with app using Microsoft Entra as IDP. If you need any testing or help, please let me know

@ctron
Copy link
Owner

ctron commented Jan 19, 2024

Ok, I dug a bit into this, the reason for this is that the Redirect component calls start_login with default options. And when evaluating the agent doesn't take into consideration the "agent configured" login options.

Good news, this should be an easy fix.

@ctron
Copy link
Owner

ctron commented Jan 19, 2024

@kate-shine @AlexandreRoba there's a PR for this now: #24 … it would be great if you could give it a try.

You should be able to do this using:

[patch.crates-io]
yew-oauth2 = { git = "https://github.com/ctron/yew-oauth2", rev = "4342e94907799da7d305492e0b7df3a8326373b4" } 

@ctron ctron closed this as completed in #24 Jan 19, 2024
ctron added a commit that referenced this issue Jan 19, 2024
@AlexandreRoba
Copy link
Contributor Author

@ctron thanks a lot for this. I was dragged into other issues. i will give it a try this we.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants