Repository: streamlit/streamlit
File: lib/streamlit/web/server/oauth_authlib_routes.py (Python)

Code before:

        return payload["provider"]


class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
    def get(self) -> None:
        self.clear_auth_cookie()

        provider_logout_url = self._get_provider_logout_url()
        if provider_logout_url:
            self.redirect(provider_logout_url)
        else:
            self.redirect_to_base()

    def _get_redirect_uri(self) -> str | None:
        auth_section = get_secrets_auth_section()
        if not auth_section or not auth_section.get("redirect_uri"):
            return None

        redirect_uri: str = auth_section["redirect_uri"]
        if not redirect_uri.endswith("/oauth2callback"):
            _LOGGER.warning("Redirect URI does not end with /oauth2callback")
            return None

        return redirect_uri

    def _get_provider_logout_url(self) -> str | None:
        """Get the OAuth provider's logout URL from OIDC metadata."""
        cookie_value = get_cookie_with_chunks(self._get_signed_cookie, AUTH_COOKIE_NAME)

        if not cookie_value:
            return None

        try:
            user_info = json.loads(cookie_value)
            provider = user_info.get("provider")
            if not provider:
                return None

            client, _ = create_oauth_client(provider)

            metadata = client.load_server_metadata()
            end_session_endpoint = metadata.get("end_session_endpoint")

            if not end_session_endpoint:
                _LOGGER.info("No end_session_endpoint found for provider %s", provider)
                return None

            # Use redirect_uri (i.e. /oauth2callback) for post_logout_redirect_uri
            # This is safer than redirecting to root as some providers seem to
            # require url to be in a whitelist /oauth2callback should be whitelisted

Review comment (security):
Do we need to include the `port` logic here @velochy from the other PR as well. Probably should consolidate and use `redirect_uri = get_redirect_uri(auth_section)`, which handles that case, right?

Code after:

        return payload["provider"]


class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
    def get(self) -> None:
        self.clear_auth_cookie()

        provider_logout_url = self._get_provider_logout_url()
        if provider_logout_url:
            self.redirect(provider_logout_url)
        else:
            self.redirect_to_base()

    def _get_redirect_uri(self) -> str | None:
        auth_section = get_secrets_auth_section()
        if not auth_section:
            return None

        redirect_uri = get_redirect_uri(auth_section)
        if not redirect_uri:
            return None

        if not redirect_uri.endswith("/oauth2callback"):
            _LOGGER.warning("Redirect URI does not end with /oauth2callback")
            return None

        return redirect_uri

    def _get_provider_logout_url(self) -> str | None:
        """Get the OAuth provider's logout URL from OIDC metadata."""
        cookie_value = get_cookie_with_chunks(self._get_signed_cookie, AUTH_COOKIE_NAME)

        if not cookie_value:
            return None

        try:
            user_info = json.loads(cookie_value)
            provider = user_info.get("provider")
            if not provider:
                return None

            client, _ = create_oauth_client(provider)

            metadata = client.load_server_metadata()
            end_session_endpoint = metadata.get("end_session_endpoint")

            if not end_session_endpoint:
                _LOGGER.info("No end_session_endpoint found for provider %s", provider)
                return None