# Create a DiscourseConnect login link

**URL:** https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290
**Category:** Integrations
**Tags:** sso, discourseconnect, how-to
**Created:** [February 15, 2019, 11:00pm UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290 "2019-02-15T23:00:14Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![Discourse](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/discourse/32/148734_2.png) [@Discourse](https://meta.discourse.org/u/Discourse)
#### Post date: [February 15, 2019, 11:00pm UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290/1 "2019-02-15T23:00:14Z")

</div>

> 🔖 This documentation explains how to create links on a [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) provider site that log users into Discourse and redirect them to a specific Discourse URL.
> 
> 🙋 Required user level: Administrator

Sites using [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) can add links on their [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) provider site that will log users into Discourse and redirect them to a specific Discourse URL. This is done by creating links that point to the `/session/sso` route that have a `return_path` parameter set to the path of the Discourse page you want users to end up on.

The link’s `href` property should be in the form below, with the path you want users to end up on substituted for `<relative_path>`:

```plaintext
https://forum.example.com/session/sso?return_path=<relative_path>

```

An example anchor tag that will log in a user and redirect them to a Discourse site’s homepage:

```plaintext
<a href="https://forum.example.com/session/sso?return_path=/">Community</a>

```

An example anchor tag that will log in a user and redirect them to the Top Topics page:

```plaintext
<a href="https://forum.example.com/session/sso?return_path=/top">Top Topics</a>

```

## How the return\_path is stored on Discourse

Discourse stores the value of the `return_path` parameter in the server session, keyed by the SSO nonce that is generated when a user visits the `/session/sso` route. At the end of the [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) authentication process, Discourse looks up the `return_path` using the nonce and redirects users to that path.

## Making the process seamless for authenticated users

When a user visits the Discourse `/session/sso` route, they are redirected to the URL set by the `discourse connect url` site setting. The [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) provider will then handle the authentication process in the same way as it would if the user had clicked the Discourse Login button.

For the authentication process to be seamless for users who are already logged in on the authentication provider site, the authentication provider’s [DiscourseConnect](https://meta.discourse.org/t/13045?silent=true) code needs to check to see if the user is logged in or not. If the user isn’t logged in, take them through the auth providers login process. If the user is already logged in, skip the login process on the auth provider site.

Here’s a commented example, using code from the [WP Discourse plugin](https://github.com/discourse/wp-discourse/blob/main/lib/sso-provider/discourse-sso.php). It demonstrates how authenticated users can be handled differently than unauthenticated users:

```php
public function sso_parse_request( $wp ) {
    // Check if Single Sign-On (SSO) is enabled in the plugin options
    if ( empty( $this->options['enable-sso'] ) ) {
        return null;
    }

    // Handle any logout requests before proceeding with SSO
    $this->handle_logout_request();

    // Check if the 'sso' and 'sig' parameters exist in the query variables
    if ( array_key_exists( 'sso', $wp->query_vars ) && array_key_exists( 'sig', $wp->query_vars ) ) {
        // Sanitize the 'sso' payload and signature to ensure they are safe for use
        $payload = sanitize_text_field( $wp->query_vars['sso'] );
        $sig = sanitize_text_field( $wp->query_vars['sig'] );

        // If the user is not logged in to WordPress, redirect to the login page
        // This ensures that users without an active session are prompted to log in to WordPress first
        if ( ! is_user_logged_in() ) {
            // Construct a URL to redirect back to after logging in
            $redirect = add_query_arg( $payload, $sig );
            // Generate the WordPress login URL with the redirect parameter
            $login = wp_login_url( esc_url_raw( $redirect ) );

            // Trigger an action before the login redirection (optional for logging or custom actions)
            do_action( 'wpdc_sso_before_login_redirect', $redirect, $login );

            // Redirect to the WordPress login page
            return $this->redirect_to( $login );
        } else {
            // If the user is already authenticated in WordPress, bypass the login process
            // and proceed with validating the SSO payload and signature.
            $sso_secret = $this->options['sso-secret'];
            $sso = new SSO( $sso_secret );
            
            // Validate the payload and signature using the SSO secret
            if ( ! ( $sso->validate( $payload, $sig ) ) ) {
                // Handle invalid SSO requests
                return $this->handle_error( 'parse_request.invalid_sso' );
            }

            // Get the current logged-in WordPress user
            $current_user = wp_get_current_user();
            // Prepare SSO parameters using the logged-in user's data
            $params = $this->get_sso_params( $current_user );

            try {
                // Generate a nonce from the payload and build the SSO login string
                $params['nonce'] = $sso->get_nonce( $payload );
                $q = $sso->build_login_string( $params );
            } catch ( \Exception $e ) {
                // Handle exceptions if there is an issue with SSO parameter generation
                return $this->handle_error( 'parse_request.invalid_sso_params', array( 'message' => esc_html( $e->getMessage() ) ) );
            }

            // Trigger an action before redirecting the user for SSO login (useful for logging)
            do_action( 'wpdc_sso_provider_before_sso_redirect', $current_user->ID, $current_user );

            // Log the SSO success if verbose logging is enabled
            if ( ! empty( $this->options['verbose-sso-logs'] ) ) {
                $this->logger->info( 'parse_request.success', array( 'user_id' => $current_user->ID ) );
            }

            // Redirect the authenticated user to the DiscourseConnect login URL with the SSO login string
            return $this->redirect_to( $this->options['url'] . '/session/sso_login?' . $q );
        }
    }

    // Return null if no SSO parameters are found in the request
    return null;
}

```

## Setting the return path to non-Discourse URLs

Discourse allows you to login a user and redirect them to a non-Discourse URL. Note that for this to work you need to add the domain to the `discourse connect allowed redirect domains` site setting. By default this setting is blank - preventing redirects to non-Discourse URLs. You can also use `*` as a wildcard to allow all domains. If you enable it, be sure to use the absolute URL in the `return_path` parameter for any non-Discourse URLs that you want to direct users to.

> Last edited by @simon 2024-09-25T07:22:09Z
> 
> > **Check document**
> >
> > Perform check on document:

---

<div class="post-metadata">

### Author: ![hosna](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/hosna/32/99969_2.png) [@hosna](https://meta.discourse.org/u/hosna)
#### Post date: [July 17, 2024, 8:33pm UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290/2 "2024-07-17T20:33:30Z")

</div>

I’m using this method but each time I need to login via sso.

How can we make it seamless, meaning if I have already logged in to discourse, there would be no need to go to sso page to login again?

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [July 18, 2024, 4:30am UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290/3 "2024-07-18T04:30:57Z")

</div>

> [@hosna](#):
>
> How can we make it seamless, meaning if I have already logged in to discourse, there would be no need to go to sso page to login again?

Unless something has changed, that is how the link is expected to work. For example, if you are logged into Discourse and click a link on the SSO provider site that points to `https://forum.example.com/session/sso?return_path=/t/some-slug/23`, you should be seamlessly redirected to `/t/some-slug/23` without having to visit the login page first.

---

<div class="post-metadata">

### Author: ![hosna](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/hosna/32/99969_2.png) [@hosna](https://meta.discourse.org/u/hosna)
#### Post date: [July 18, 2024, 6:43am UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290/4 "2024-07-18T06:43:03Z")

</div>

I’m already on the latest update of discourse and this is how the sso works for me:

As you can see I’m already logged in, but when I enter a url like `https://forum.example.com/session/sso?return_path=/t/some-slug/23`, I would be redirected to sso login page again.

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [July 18, 2024, 12:54pm UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290/5 "2024-07-18T12:54:55Z")

</div>

I think what is happening is that when you visit a route like `https://forum.example.com/session/sso?return_path=/t/some-slug/23`, Discourse redirects you to the `discourse connect url`, regardless of whether you are logged into Discourse or not. That happens here:

> <https://github.com/discourse/discourse/blob/main/app/controllers/session_controller.rb#L25-L42>

The SSO provider site is then expected to handle the case of users who are already logged into the site. Here’s how the [WP Discourse](https://github.com/discourse/wp-discourse) plugin handles it:

> <https://github.com/discourse/wp-discourse/blob/main/lib/sso-provider/discourse-sso.php#L148-L171>

That code (what follows the `else` statement) handles the case of users who are already logged into WordPress. They are redirected back to the URL that’s supplied by the `return_path` query param. So from the user’s point of view, they are taken directly to the return path URL, but what actually happens is that they are redirected to the SSO provider site, then back to Discourse.

I _think_ the problem on your site is that your SSO code isn’t handling the case of users who are already logged into the site.

I don’t have things setup to test this right now. It’s possible that I’m reading the code incorrectly. Before looking at the code, I thought that a check was run on the Discourse end to see if the user was already logged into Discourse, but that does not seem to be the way it works.

---

<div class="post-metadata">

### Author: ![hosna](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/hosna/32/99969_2.png) [@hosna](https://meta.discourse.org/u/hosna)
#### Post date: [July 18, 2024, 4:24pm UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290/6 "2024-07-18T16:24:19Z")

</div>

thank you very much for your explanation.

> [@simon](#):
>
> I _think_ the problem on your site is that your SSO code isn’t handling the case of users who are already logged into the site.

Yes that is a thing we are going to fix in our sso.

However

> [@simon](#):
>
> I thought that a check was run on the Discourse end to see if the user was already logged into Discourse

I think having this checked on discourse side would have created a better user experience.

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [September 25, 2024, 7:26am UTC](https://meta.discourse.org/t/create-a-discourseconnect-login-link/109290/7 "2024-09-25T07:26:11Z")

</div>

I edited the OP to add details about how the process works, and how to handle the case of users who are already authenticated on the SSO provider site. It’s a question that’s come up a few times.

Possibly a less detailed version of this topic could be integrated into [Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #482](https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045/482). The `return_path` query param isn’t mentioned in that topic.
