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

Sign in Workflow with auth token or cookie redirect/payload #2346

Open
corsacca opened this issue Jan 29, 2024 · 13 comments · May be fixed by #2548
Open

Sign in Workflow with auth token or cookie redirect/payload #2346

corsacca opened this issue Jan 29, 2024 · 13 comments · May be fixed by #2548

Comments

@corsacca
Copy link
Member

No description provided.

@zdmc23
Copy link

zdmc23 commented Jan 29, 2024

Here is an example of ChatGPT using Auth0 for a web browser based mobile login flow:

trim.4C77FAA8-3C2E-42B6-8933-6017B007025F.MOV

@zdmc23
Copy link

zdmc23 commented Jan 29, 2024

@zdmc23
Copy link

zdmc23 commented Jan 29, 2024

For our purposes, since we also want to support a refresh_token, then we could probably just use the same endpoint (and grant_type=refresh_token):

https://developer.okta.com/docs/reference/api/oidc/#token

So, when that endpoint is accessed with that grant_type, then we check for the refresh token in the request. If it is present and valid, then issue new JWT (access and refresh tokens). If not (here is where the cookie exchange comes into play), we could validate the cookie, and assuming it is valid, then we also return a new JWT (access and refresh tokens), but this time via a more secure redirect that only the app will be handling. Then from that point forward, the mobile app will attempt to refresh the tokens according to the normal flow mentioned above (to keep the user logged in for as long as they actively use the app).

@corsacca
Copy link
Member Author

THanks @zdmc23

would the refresh_token be able to happen in the background, so the user would not experience the redirects?

@zdmc23
Copy link

zdmc23 commented Jan 30, 2024

THanks @zdmc23

would the refresh_token be able to happen in the background, so the user would not experience the redirects?

@corsacca Yes, with each API request, we could check the expiration date of the token, and just refresh it when it is getting close to expire. The user never notices anything different. Linking it to the usage is desirable, bc most often we do want the token to expire when they haven't been actively using the app/API (for security sake, so they haven't simply lost their phone or a hostile family member starts randomly checking apps, etc...)

@zdmc23
Copy link

zdmc23 commented Feb 5, 2024

trim.66088B82-74DD-4E1A-BFC3-A011F411A837.MOV

@zdmc23
Copy link

zdmc23 commented Feb 5, 2024

@corsacca I think we can make this work without many changes. It relies on the redirect_to parameter at login:

https://example.com/wp-login.php?redirect_to=/wp-json/jwt-auth/v1/token

Upon successful login (whether standard username/password, MFA, SSO, whichever plugins are being used, so long as the standard WordPress redirect_to is being honored), it will redirect to the new endpoint which exchanges a valid session (ie, cookie) for a JWT access token.

Here is the code snippet to make it happen:

All edits would be in: https://github.com/DiscipleTools/disciple-tools-theme/blob/develop/dt-core/libraries/wp-api-jwt-auth/public/class-jwt-auth-public.php

        /**
         * Add the endpoints to the API
         */
        public function add_api_routes() {
                register_rest_route( $this->namespace, 'token', [
                        'methods'             => 'GET',
                        'callback'            => [ $this, 'exchange_cookie_for_jwt' ],
                        'permission_callback' => '__return_true',
                ] );

                register_rest_route( $this->namespace, 'token', [
                        'methods'             => 'POST',
                        'callback'            => [ $this, 'generate_token' ],
                        'permission_callback' => '__return_true',
                ] );

                register_rest_route( $this->namespace, 'token/refresh', [
                        'methods'             => 'POST',
                        'callback'            => [ $this, 'refresh_access_token' ],
                        'permission_callback' => '__return_true'
                    ]
                );

                register_rest_route( $this->namespace, 'token/validate', [
                        'methods'             => 'POST',
                        'callback'            => [ $this, 'validate_token' ],
                        'permission_callback' => '__return_true',
                ] );
        }

        public function refresh_access_token(WP_REST_Request $request) {
                if ( !$this->has_permission() ) {
                    return new WP_Error( __FUNCTION__, "You do not have permission for this", [ 'status' => 403 ] );
                }
                $this->validate_token( $request );
                $token = $this->generate_token_static( 'dummy-email', 'dummy-password' );

                //remove_filter( 'authenticate', [ $this, 'allow_programmatic_login' ], 10 );

                if ( $token ) {
                    return [
                        'login_method' => DT_Login_Methods::MOBILE,
                        'jwt' => $token,
                    ];
                }
        }

        public function exchange_cookie_for_jwt(WP_REST_Request $request) {
                //if ( !is_user_logged_in() ) {
                if ( !wp_validate_auth_cookie() ) {
                        wp_redirect( '/wp-login.php' );
                        exit();
                }
                $token = $this->generate_token_static( 'dummy-email', 'dummy-password' );
                wp_redirect( 'exp://127.0.0.1:8081/?token=' . $token );
                //wp_redirect( 'discipletools://example.com/?token=' . $token );
                //wp_redirect( 'dt://example.com/?token=' . $token );
                exit();
        }
  • @squigglybob , bc it looks like you have coded/refactored a decent amount of the login functionality
  • @cairocoder01 , bc you were in the previous Slack thread

@zdmc23
Copy link

zdmc23 commented Feb 5, 2024

A couple of notes:

  1. the Redirect URI for the App probably needs to be configurable (not sure whether you have any preferences on where that belongs):

    • For Dev, it looks like: exp://127.0.0.1:8081.
    • For Prod, it will look like: dt://example.com/ (more obscure for security sake, but also greater chance of collision with other Apps since only 2 letter protocol), or discipletools://example.com (whichever you prefer that we use)
    • (or have the Prod protocol hardcoded and allow for a Dev override. I thought about having another URL query param for it, but I don't think we can be confident that 3rd party plugins forward on URL params the way that they should with redirect_to since it is a WP standard)
  2. currently generate_token requires email and password, but it probably shouldn't. That's not required for a JWT token. So we either need to change that, or update the code above to pull the User email and credentials and then pass them so the JWT token can be generated on "refresh"

  3. feel free to refactor the above however it should be to fit the D.T standard. In some sense it is pseudocode, bc I needed to comment out things like (bc it did not work to validate the cookie like I expected, etc...):

    if ( !wp_validate_auth_cookie() ) {
            wp_redirect( '/wp-login.php' );
            exit();
    }

Thx!

@zdmc23
Copy link

zdmc23 commented Feb 5, 2024

Some other related things to eventually discuss, but not urgent:

  • the current access token is set to expire by default after 1 week. We could probably make that shorter now that we will have a refresh capability
  • depending on default expiration, and anticipated user patterns, we could refresh the token every time the app loads (this would make sense if we keep the 1 wk exp), or it can be refreshed every so often in the background, or as much as every request (a bit excessive :-) )
  • normally, there is a Bearer Token JWT which includes both Access Token and separate Refresh Token. It is fine how we have it, but if you ever wanted to match like OAuth2 standards, etc... then we could eventually do that.
{
    "access_token" : "ey...onNtiw",
    "token_type" : "Bearer",
    "expires_in" : 3600,
    "scope"      : "openid email",
    "refresh_token" : "ey...kk2VdY",
    "id_token" : "ey...kpOurg"
}

@zdmc23
Copy link

zdmc23 commented Feb 5, 2024

trim.C70F5CDB-0585-45B7-BB06-ABA6EBDF9AB0.MOV

^ sorry, I realized the prev video did not show the actual WP login form flow (bc of a prev login attempt caching the cookie into the ios jar), but here is an example that also includes MFA with TOTP. It works nicely

@corsacca corsacca added this to D.T Dev Feb 14, 2024
@github-project-automation github-project-automation bot moved this to Backlog in D.T Dev Feb 14, 2024
@corsacca corsacca moved this from Backlog to Assigned in D.T Dev May 22, 2024
@corsacca corsacca moved this from Assigned to Next in D.T Dev May 28, 2024
@corsacca
Copy link
Member Author

corsacca commented Oct 3, 2024

Hey @zdmc23,
I had a go at implementing the cookie exchange and refresh endpoints using your code above.
See #2586
you can try the GET wp-json/jwt-auth/v1/token
and POST wp-json/jwt-auth/v1/token/refresh

@kodinkat implemented some auto refresh strategy: #2548

I'm looking for what the mvp would be to get this working.

Signing in from the website will be helpful in any app or external server.

I need to double check how our SSO strategy could workflow works with this too.

@zdmc23
Copy link

zdmc23 commented Oct 3, 2024

@corsacca Thx! What is the best way to download the theme for this particular commit hash? I can test it locally with my previous tester mobile app to try to confirm it

@corsacca
Copy link
Member Author

corsacca commented Oct 4, 2024

Most efficient? Checkout the auth-redirect branch of the theme.
otherwise direct download: https://github.com/DiscipleTools/disciple-tools-theme/archive/refs/heads/auth-redirect.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Next
Development

Successfully merging a pull request may close this issue.

2 participants