Hacks & Customizations
These hacks are unsupported customizations meant as unofficial workarounds.
They can cause instability, introduce issues and may conflict with future updates. Apply at your own risk!

Azure/Entra OIDC User Avatar Images on Initial Login

  • Author: @ssddanbrown
  • Created: 1st Nov 2025
  • Updated: 1st Nov 2025
  • Last Tested On: v25.07.3

This hack hooks into the OIDC login process to fetch and assign user avatar images from Azure/Entra on first user login/registration.

OIDC user avatar images have been officially supported in BookStack since v25.05, but this does not work for Microsoft Azure since it requires a process which does not strictly meet the OIDC specification.

Considerations

  • This will only run when the user account is initially created via first login.
    • It will not run if the user account already exists, or if it’s created by an admin user.
  • This is designed to work with either PNG or JPEG user avatar images.

Code

functions.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

use BookStack\Facades\Theme;
use BookStack\Http\HttpRequestService;
use BookStack\Theming\ThemeEvents;
use BookStack\Uploads\UserAvatars;
use BookStack\Users\Models\User;
use GuzzleHttp\Psr7\Request;

// Variable to track the access token for later use
$accessToken = '';

// Listen for the OIDC ID token validation events so we can capture the access token
Theme::listen(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, function (array $idTokenData, array $accessTokenData) use (&$accessToken) {
    $accessToken = $accessTokenData['access_token'] ?? '';
});

// Listen for the auth register event to download and assign the profile image to the user
Theme::listen(ThemeEvents::AUTH_REGISTER, function (string $authSystem, User $user) use (&$accessToken) {
    if ($authSystem === 'oidc' && $accessToken) {
        downloadAndAssignUserAvatar($user, $accessToken);
    }
});

// Function to download and assign the profile image to the user
function downloadAndAssignUserAvatar(User $user, string $accessToken): void
{
    // Create the HTTP client for fetching the profile image
    /** @var HttpRequestService $http */
    $http = app()->make(HttpRequestService::class);
    $client = $http->buildClient(4);

    // Fetch the profile image via an authorized request
    $response = $client->sendRequest(new Request('GET', 'https://graph.microsoft.com/v1.0/me/photo/$value', [
        'Authorization' => 'Bearer ' . $accessToken,
    ]));

    // If the response is successful and the content type is an image, assign the image to the user
    $allowedContentTypes = ['image/jpeg', 'image/png'];
    if ($response->getStatusCode() === 200 && in_array($response->getHeader('Content-Type')[0], $allowedContentTypes)) {
        $avatars = app()->make(UserAvatars::class);
        $extension = explode('/', $response->getHeader('Content-Type')[0])[1];
        $avatars->assignToUserFromExistingData($user, $response->getBody()->getContents(), $extension);
    }
}

Request an Update

Hack not working on the latest version of BookStack?
You can request this hack to be updated & tested for a small one-time fee.
This helps keeps these hacks updated & maintained in a sustainable manner.


Latest Hacks