Librenms: Microsoft Oauth

So, after eventually falling blank with the documentations on getting the Socialite configs working with Microsoft Entra ( specifically the role mappings ). I had to patch the Socialite code to get it to work. The way Libre Maps scopes etc and groups is a bit backwards. But to get this to work. Set up per the documentations with Librenms.

lnms plugin:add socialiteproviders/microsoft

lnms config:set auth.socialite.configs.microsoft.client_id ds22389d-cea2-4a78-ab19-ee11213e90b1725
lnms config:set auth.socialite.configs.microsoft.client_secret 71148Q~L0Z21313eph0nS2kc7231sddddd2PmlaM9
lnms config:set auth.socialite.configs.microsoft.tenant 912sds56-8223-481112-8658e-4115166eb5

lnms config:set auth.socialite.configs.microsoft.listener “\SocialiteProviders\Microsoft\MicrosoftExtendSocialite”

lnms config:set auth.socialite.default_role global-read

lnms config:set auth.socialite.claims.sdf3c8a-9c2a-43sd-92c1-43fsdcc515d.roles ‘[“admin”]’

lnms config:set auth.socialite.scopes.0 “openid”
lnms config:set auth.socialite.scopes.1 “profile”
lnms config:set auth.socialite.scopes.2 “email”
lnms config:set auth.socialite.scopes.3 “https://graph.microsoft.com/User.Read”
lnms config:set auth.socialite.scopes.4 “https://graph.microsoft.com/Group.Read.All”

[ This is example data]

Make sure your user is part of the group you are using for the claims.

Now on your group claims:

click the hotdog, and edit:

you need to emit the claims as roles:

You now need to patch the Socialite Controller to actually read these.

/opt/librenms/app/Http/Controllers/Auth/SocialiteController.php

Look for the function setRolesFromClaim and add as per the comments below:

private function setRolesFromClaim(string $provider, $user): bool
{
    $scopes = LibrenmsConfig::get('auth.socialite.scopes');
    $claims = LibrenmsConfig::get('auth.socialite.claims');

    if (is_array($scopes) &&
        $this->socialite_user instanceof \Laravel\Socialite\AbstractUser &&
        ! empty($claims)
    ) {
        $roles = [];
        $attributes = $this->socialite_user->getRaw();

        if (is_object(current($attributes)) && method_exists(current($attributes), 'getName') && method_exists(current($attributes), 'getAllAttributeValues')) {
            $parsed_attributes = [];
            foreach ($attributes as $attribute_object) {
                $attribute_name = $attribute_object->getName();
                $attribute_values = $attribute_object->getAllAttributeValues();
                $parsed_attributes[$attribute_name] = $attribute_values;
            }
            $attributes = $parsed_attributes;
        }

        // CHECK FOR 'roles' ATTRIBUTE EXPLICITLY (for Microsoft groups emitted as roles)
        if (isset($attributes['roles']) && is_array($attributes['roles'])) {
            foreach ($attributes['roles'] as $group_id) {
                $roles = array_merge($roles, $claims[$group_id]['roles'] ?? []);
            }
        }

        // ORIGINAL LOGIC - check scope matching
        foreach ($scopes as $scope) {
            foreach ($attributes as $attribute_name => $attribute_values) {
                if (str_contains($attribute_name, $scope)) {
                    foreach (Arr::wrap($attributes[$attribute_name] ?? []) as $scope_data) {
                        $roles = array_merge($roles, $claims[$scope_data]['roles'] ?? []);
                    }
                }
            }
        }

        if (count($roles) > 0) {
            $user->syncRoles(array_unique($roles));

            return true;
        }
    }

    return false;
}

Hope this helps others that have been trying to get this working.

I’ve submitted a pull request to get this into the code base. Will see if its accepted.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.