jiachengzhang1 / Umbrella.jl

A Simple Authentication plugin for any Web Frameworks in Julia
https://jiachengzhang1.github.io/Umbrella.jl
MIT License
15 stars 3 forks source link

Add parameters to redirect #7

Open JBlaschke opened 11 months ago

JBlaschke commented 11 months ago

This PR introduces the following pattern for verifying tokens and redirecting on success:

            params = verify(tokens, profile)
            redirect_hanlder(config.success_redirect, params)

The idea of this pattern is that the algorithm behind verfiy returns a Dict of parameters -- which can be anything. Here I am imagining that this includes a unique hash representing the result of token verification (e.g. a key to a lookup table). This can then be used by the page located at config.success_redirect to look up any data stored by verify. Note thatparams is allowed to be nothing (e.g. if token validation failed, or if this pattern is unused). If params is nothing then no parameters are included by redirect_handler.

A simple use case for this is if verify stores the logged in user's name using a global dict. The value in params can then be used to look up the user's name. Eg:

struct Users
    tokens::Dict{String, Tuple{String, String, String}}
    Users() = new(Dict{String, Tuple{String, String, String}}())
end

function add(
        users::Users, 
        email::String, access_token::String, refresh_token::String;
        length=16
    )
    while true
        new_key = randstring(['0':'9'; 'a':'f'], length)
        if ! (new_key in keys(users.tokens))
            users.tokens[new_key] = (email, access_token, refresh_token)
            return new_key
        end
    end
end

import Base.keys

keys(u::Users) = Base.keys(u.tokens)
email(u::Users, k::String) = u.tokens[k][1]
access_token(u::Users, k::String) = u.tokens[k][2]
refresh_token(u::Users, k::String) = u.tokens[k][3]

authenticated_users = Users()

@get oauth_callback function(req)
    query_params = queryparams(req)
    code = query_params["code"]

    # handle tokens and user details
    google_oauth2.token_exchange(code,
        function (tokens::Google.Tokens, user::Google.User)
            user_key = add(
                authenticated_users,
                user.email, tokens.access_token, tokens.refresh_token
            )
            return Dict("user_key" => user_key)
        end
    )
end

function is_authenticated(req)
    query_params = queryparams(req)
    if !("user_key" in keys(query_params)) return false end
    return query_params["user_key"] in keys(authenticated_users)
end

@get "/protected" function(req)
    if is_authenticated(req)
        user_key = queryparams(req)["user_key"]
        """
        <html>
            <body>
                You are successfully signed in as: $(email(authenticated_users, user_key))
                <p>
                Please go to: <a href='$(oauth_path)'>Authenticate with Google</a> to log in as a different user
            </body>
        </html>
        """
    else
        """
        <html>
            <body>
                You are not logged in, please log in by going to:
                <a href='$(oauth_path)'>Authenticate with Google</a>
            </body>
        </html>
        """
    end
end

In this case params contains only the user UUID -- but it can be whatever your application needs.

I'm thinking of how to generalize this to include a session cookie in the redirect_handler.

JBlaschke commented 7 months ago

Hi @jiachengzhang1 any chance you can take a look at this PR?