WP Discourse SSO does not synchronize local avatars provided by Advanced Site Enhancements

Hello,

I am using:

  • WordPress
  • WP Discourse (latest version)
  • Advanced Site Enhancements (ASE), using its Local User Avatar feature (no Gravatar)

The avatar is correctly displayed everywhere in WordPress.

ASE stores the attachment ID in the user meta field:

local_user_avatar_attachment_id

During WordPress SSO login, however, Discourse always imports the default avatar instead of the local avatar.

I already tried using the wpdc_sso_avatar_url filter to return the media library URL, but it made no difference.

Therefore I have two questions:

  1. Does WP Discourse still use wpdc_sso_avatar_url for avatar synchronization?
  2. Which function or filter is currently responsible for determining the avatar URL sent to Discourse?

Any guidance would be appreciated.

Hey @miednr, yes, wpdc_sso_avatar_url is still used for avatar sync. Let’s see if we can get it working for you.

  1. Do you have Force Avatar Update in the DiscourseConnect Provider settings enabled?
  2. Please share the function you’re using with wpdc_sso_avatar_url

Hello @angus !

Thank you for your attention to this. Yes, I have “Force Avatar Update” enabled.

My function at this point in time is:



<?php
add_filter( 'get_avatar_url', function( $url, $id_or_email, $args ) {
    $user = false;
    if ( is_numeric( $id_or_email ) ) {
        $user = get_user_by( 'id', (int) $id_or_email );
    } elseif ( $id_or_email instanceof WP_User ) {
        $user = $id_or_email;
    }
    if ( ! $user ) {
        return $url;
    }
    $avatar_id = get_user_meta( $user->ID, 'local_user_avatar_attachment_id', true );
    if ( ! $avatar_id ) {
        return $url;
    }
    $local_avatar_url = wp_get_attachment_image_url( (int) $avatar_id, 'full' );
    return $local_avatar_url ?: $url;
}, 10, 3 );

I changed it some times, to make it work. But, I guess, I’ve had a better version before. :wink:

Would be great, if we could find a working version.

Did you perhaps share the wrong snippet? That code is using the get_avatar_url filter. As you observed, the filter is wpdc_sso_avatar_url.

Hello @angus

I shard a different approach, that I tried. Sorry. This is the best snippet, I can share:

add_filter( 'wpdc_sso_avatar_url', function( $avatar_url, $user_id ) {

$avatar_id = get_user_meta( $user_id, 'local_user_avatar_attachment_id', true );

if ( ! $avatar_id ) {
    return $avatar_url;
}

$local_avatar_url = wp_get_attachment_image_url( (int) $avatar_id, 'full' );

if ( ! $local_avatar_url ) {
    return $avatar_url;
}

return esc_url_raw( $local_avatar_url );

}, 10, 2 );

I tried this snippet. ASE stores the local avatar attachment ID in the user meta field local_user_avatar_attachment_id. The attachment ID exists and points to a media library image. However, Discourse still imports the default avatar after WordPress login/SSO sync.

I also tried the same approach with the thumbnail image size instead of full, but the result was the same.

Thank you for having a look!

Hi @angus,

I have received a response from the ASE (Advanced Site Enhancements) developer that may help narrow this down.

According to them:

* ASE hooks into the standard WordPress avatar APIs:

* get_avatar (priority 5)

* get_avatar_url (priority 10)

* get_avatar_data (priority 10)

* There are currently no known compatibility issues with other plugins.

* The local avatar is stored in the Media Library, and the attachment ID is saved in the user meta field:

local_user_avatar_attachment_id

They also suggested manually retrieving the avatar URL like this:

$attachment_id = (int) get_user_meta( $user_id, 'local_user_avatar_attachment_id', true );

if ( $attachment_id ) {

$avatar_url = wp_get_attachment_image_url( $attachment_id, array( 128, 128 ) );

}

I also tried using this inside the wpdc_sso_avatar_url filter, but the result was unchanged—the default Discourse avatar is still used after login.

Since ASE claims to hook into get_avatar_url(), I was wondering:

* Does WP Discourse call get_avatar_url() before or after applying the wpdc_sso_avatar_url filter?

* Is there anything specific about the avatar URL (size, accessibility, timing, etc.) that Discourse expects during avatar synchronization?

I’d be happy to run any additional tests or add temporary debugging if that would help.

Thanks again for your help!

Do you have the discourse_connect_overrides_avatar site setting enabled in Discourse?

Yes, it has been activated.

Ok, let’s check what’s in the final SSO params. Please add this filter and check what it logs

add_filter( 'wpdc_sso_params', function ( $params ) {
   error_log( "avatar_url: {$params['avatar_url']} force: {$params['avatar_force_update']}" );
   return $params;
});

The snippet is loaded on every page load, but wpdc_sso_params never fires during logout/login or during the Discourse login flow.

If that filter never fires then it is likely that you don’t have DiscourseConnect setup properly, or you’re not using Wordpress as the provider to login to Discourse. Please share the meta file from WP Discourse > Logs.

Hello @angus

sure, please have a look:

### Server ###

WordPress - 7.0

PHP - 8.3.6

MySQL - 8.4.9

### WP Discourse Settings (Secrets Excluded) ###

connection-logs - 0

display-subcategories - 1

allow-tags - 1

max-tags - 15

publish-as-unlisted - 0

full-post-content - 0

custom-excerpt-length - 100

add-featured-link - 1

auto-publish - 1

force-publish - 1

force-publish-max-age - 0

publish-failure-notice - 1

auto-track - 1

allowed_post_types - post

exclude_tags -

hide-discourse-name-field - 0

discourse-username-editable - 0

direct-db-publication-flags - 0

verbose-publication-logs - 0

enable-discourse-comments - 1

comment-type - display-public-comments-only

ajax-load - 1

load-comment-css - 1

discourse-new-tab - 1

hide-wordpress-comments - 1

show-existing-comments - 0

max-comments - 10

min-replies - 0

min-score - 0

min-trust-level - 0

bypass-trust-level-score - 0

only-show-moderator-liked - 0

custom-datetime-format -

cache-html - 0

clear-cached-comment-html - 0

verbose-comment-logs - 0

use-discourse-webhook - 1

use-discourse-user-webhook - 1

webhook-match-user-email - 0

verbose-webhook-logs - 0

verbose-sso-logs - 1

enable-sso - 1

auto-create-sso-user - 1

real-name-as-discourse-name - 0

force-avatar-update - 1

redirect-without-login - 0

sso-client-enabled - 0

sso-client-login-form-change - 0

sso-client-sync-by-email - 0

sso-client-disable-create-user - 0

sso-client-sync-logout - 0

logs-enabled - 1

Wordpress is indeed set up as the provider, but, seemingly, the DiscourseConnect code isn’t firing. This suggests to me that you’re not actually logging in using DiscourseConnect, or you’re not loading the snippets or logs correctly. Do you perhaps have some other login method setup, such as OAuth? Perhaps record exactly what you’re doing to test this. Try to capture the urls in the recording and / or by enabling “Preserve Log” in the dev tools network tab.

but wpdc_sso_params never fires during logout/login or during the Discourse login flow

Also, please share how you came to this conclusion.