bleumink / streamlit-keycloak

User authentication and single sign-on in your Streamlit app using Keycloak
MIT License
59 stars 11 forks source link

Consider implementing additional options for login control and providing support for logging out #13

Open mapix opened 1 year ago

mapix commented 1 year ago

Example case:

I have already implemented these changes in my forked branch. If they are well-received, I would be happy to create a pull request to merge them into the upstream branch.

https://github.com/bleumink/streamlit-keycloak/compare/master...pragmatic-streamlit:streamlit-keycloak:feat/allow-logout

bleumink commented 1 year ago

Great stuff, I would happily merge this in. For the logout functionality, we could also go all the way:

hansehe commented 1 year ago

Hi, I can see that this feature is merged into the branch feature/logout, and we are working on something that highly depend on using additional login options (basically setting the idp hint option during login). Is it possible to get this functionality out as a beta package or something soon? Maybe even in the master branch?

bleumink commented 1 year ago

Would providing the id token be enough for that? If so, I can merge that into master first. I was looking at the logout functionality as well, but had some issues getting keycloak-js to generate working logout urls.

edit: and also the login_options of course

bleumink commented 1 year ago

Merged the changes into master, should have done that earlier. I will look at the logout functionality soon.

hansehe commented 1 year ago

Hi again, I'm trying to use the login_options but it doens't seem to work. So this is the input to the streamlit keycloak login:

keycloak = login(    
            url=settings.url,
            realm=settings.realm,
            client_id=settings.client_id,
            login_options={
                "idpHint": settings.kc_idp_hint,
                "loginHint": "myemail@gmail.com",
            },
            auto_refresh = True
    )   

I'm expecting the 'kc_idp_hint' query parameter to be provided with the login url to keycloak, including the login hint. However none of the parameters are provided in the login url. I've double checked the streamlit keycloak code and I cannot find the reason to the problem. Are you able to understand why it doesn't work?

Thanks a lot for your contributions.

hansehe commented 1 year ago

Think I found the bug, and created a PR on it: https://github.com/bleumink/streamlit-keycloak/pull/18

CHerSun commented 1 year ago

Could you elaborate on how to log out?

michal-sensestreet commented 1 year ago

I second the Logout question.

mapix commented 1 year ago

@CHerSun @michal-sensestreet
The following example demonstrates the login and logout of streamlit keycloak, the entire experience is very smooth.

import os
from urllib.parse import urlencode

import streamlit as st
from streamlit.runtime import Runtime
from streamlit.runtime.scriptrunner import get_script_run_ctx

from streamlit_keycloak import login

st.title("Streamlit Keycloak Login & Logout example")

def _get_session_id():
    context = get_script_run_ctx()
    if not context:
        return
    return context.session_id

def _get_current_request():
    session_id = _get_session_id()
    if not session_id:
        return None
    runtime = Runtime._instance
    if not runtime:
        return
    client = runtime.get_client(session_id)
    if not client:
        return
    return client.request

def get_web_origin():
    request = _get_current_request()
    return request.headers["Origin"] if request else os.getenv("WEB_BASE", "")

keycloak_endpoint = "https://example.com"
keycloak_realm = "example"

if not st.session_state.get("keycloak_user_info"):
    keycloak = login(
        url=keycloak_endpoint,
        realm=keycloak_realm,
        client_id="example_client",
        init_options={"checkLoginIframe": False},
    )
    if keycloak.authenticated:
        st.session_state.keycloak_id_token = keycloak.id_token
        st.session_state.keycloak_user_info = keycloak.user_info

if st.session_state.get("keycloak_user_info"):
    params = urlencode(
        {
            "post_logout_redirect_uri": get_web_origin(),
            "id_token_hint": st.session_state.keycloak_id_token,
        }
    )
    st.json(st.session_state.keycloak_user_info)
    st.markdown(
        f'Switch account by <a target="_self" href="{keycloak_endpoint}/realms/{keycloak_realm}/protocol/openid-connect/logout?{params}">Logout</a>',
        unsafe_allow_html=True,
    )
michal-sensestreet commented 1 year ago

thanks @mapix , works like a charm.

CHerSun commented 1 year ago

@mapix thank you! Awesome, love the target="_self" too to make everything work in the same tab.

Do you maybe know if it's possible to login using same tab only too? I.e. without popup on desktop browsers (mobile browsers seem to utilize same tab).