Okta group mapping isn't working

Configured Okta provider in LibreNMS 24.4.0 to provide authentication to user through Okta.

It currently works but we would like to have group to role mapping which’s not working

I followed this article Oauth/SAML support - LibreNMS Docs

and I see PR Add support for Okta Group claims to set Roles by peejaychilds · Pull Request #15592 · librenms/librenms · GitHub does what we’re looking for

but when user authenticate through Okta it only assign the default role ignoring the role given to groups

Here are few pictures show our configs

even thought I’m in sg-remote-users group but when I authenticate through Okta is assign me global-read role

Also I’m keeping authorization method to mysql to not lose admin access (not sure if that’s ok)

#!/usr/bin/env php
<?php

/*
 * LibreNMS
 *
 * Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.  Please see LICENSE.txt at the top level of
 * the source code distribution for details.
 */

use LibreNMS\Config;
use LibreNMS\ValidationResult;
use LibreNMS\Validator;

chdir(__DIR__); // cwd to the directory containing this script

ini_set('display_errors', 1);

$options = getopt('g:m:s::h::');

if (isset($options['h'])) {
    echo
    "\n Validate setup tool

    Usage: ./validate.php [-g <group>] [-s] [-h]
        -h This help section.
        -s Print the status of each group
        -g Any validation groups you want to run, comma separated:
          Non-default groups:
          - mail: this will test your email settings  (uses default_mail option even if default_only is not set)
          - distributedpoller: this will test for the install running as a distributed poller
          - rrdcheck: this will check to see if your rrd files are corrupt
          Default groups:
          - configuration: checks various config settings are correct
          - database: checks the database for errors
          - dependencies: checks that all required libraries are installed and up-to-date
          - disk: checks for disk space and other disk related issues
          - php: check that various PHP modules and functions exist
          - poller: check that the poller and discovery are running properly
          - programs: check that external programs exist and are executable
          - python: check that various Python modules and functions exist
          - system: checks system related items
          - updates: checks the status of git and updates
          - user: check that the LibreNMS user is set properly

        Example: ./validate.php -g mail.

        ";
    exit;
}

if (function_exists('posix_getuid') && posix_getuid() === 0) {
    echo 'Do not run validate.php as root' . PHP_EOL;
    exit(1);
}

// Check autoload
if (! file_exists('vendor/autoload.php')) {
    print_fail('Composer has not been run, dependencies are missing', './scripts/composer_wrapper.php install --no-dev');
    exit;
}

require_once 'vendor/autoload.php';
require_once 'includes/common.php';
require_once 'includes/functions.php';

// Buffer output
ob_start();
$precheck_complete = false;
register_shutdown_function(function () {
    global $precheck_complete;

    if (! $precheck_complete) {
        // use this in case composer autoloader isn't available
        spl_autoload_register(function ($class) {
            @include str_replace('\\', '/', $class) . '.php';
        });
        print_header();
    }
});

$pre_checks_failed = false;

// config.php checks
if (file_exists('config.php')) {
    $syntax_check = `php -ln config.php`;
    if (strpos($syntax_check, 'No syntax errors detected') === false) {
        print_fail('Syntax error in config.php');
        echo $syntax_check;
        $pre_checks_failed = true;
    }

    $first_line = rtrim(`head -n1 config.php`);
    if (! strpos($first_line, '<?php') === 0) {
        print_fail("config.php doesn't start with a <?php - please fix this ($first_line)");
        $pre_checks_failed = true;
    }
    if (strpos(`tail config.php`, '?>') !== false) {
        print_fail('Remove the ?> at the end of config.php');
        $pre_checks_failed = true;
    }
}

// Composer check
$validator = new Validator();
$validator->validate(['dependencies']);
if ($validator->getGroupStatus('dependencies') == ValidationResult::FAILURE) {
    $pre_checks_failed = true;
}

if ($pre_checks_failed) {
    exit;
}

$init_modules = [];
require 'includes/init.php';

// make sure install_dir is set correctly, or the next includes will fail
if (! file_exists(Config::get('install_dir') . '/.env')) {
    $suggested = realpath(__DIR__);
    print_fail('\'install_dir\' config setting is not set correctly.', "It should probably be set to: $suggested");
    exit;
}

if (\LibreNMS\DB\Eloquent::isConnected()) {
    $validator->ok('Database connection successful', null, 'database');
} else {
    $validator->fail('Error connecting to your database.', null, 'database');
}

$precheck_complete = true; // disable shutdown function
print_header();

if (isset($options['g'])) {
    $modules = explode(',', $options['g']);
} elseif (isset($options['m'])) {
    $modules = explode(',', $options['m']); // backwards compat
} else {
    $modules = []; // all modules
}

// run checks
$validator->validate($modules, isset($options['s']) || ! empty($modules));

function print_header()
{
    $output = ob_get_clean();
    @ob_end_clean();

    echo \LibreNMS\Util\Version::get()->header() . PHP_EOL;
    echo $output;
}

// output matches that of ValidationResult
function print_fail($msg, $fix = null)
{
    echo "[\033[31;1mFAIL\033[0m]  $msg";
    if ($fix && strlen($msg) > 72) {
        echo PHP_EOL . '       ';
    }

    if (! empty($fix)) {
        echo " [\033[34;1mFIX\033[0m] \033[34;1m$fix\033[0m";
    }
    echo PHP_EOL;
}

The OKTA side needs to be configured with which groups are returned

You can see if there is any group data returned by adding a dumping function.

I am in many groups, but the person that configured our Okta integration specifically allowed two groups, and it only returns the one of those that I am in.

*** SocialiteController.php.orig	Fri Jun  7 04:45:35 2024
--- SocialiteController.php	Fri Jun  7 04:48:02 2024
***************
*** 162,167 ****
--- 162,171 ----
              $roles = [];
              $attributes = $this->socialite_user->getRaw();

+             $fileHandle = fopen('/tmp/userdump', 'a');
+             fwrite($fileHandle, json_encode($attributes));
+             fclose($fileHandle);
+
              foreach ($scopes as $scope) {
                  foreach (Arr::wrap($attributes[$scope] ?? []) as $scope_data) {
                      $roles = array_merge($roles, $claims[$scope_data]['roles'] ?? []);

Replied on reddit too but as @pjchilds said, Okta needs to be configured to send over the groups claim. His snippet will help you confirm what’s getting sent back. The okta side needs to be configured on the sign-on tab of the app.

1 Like

thank you both @pjchilds and @dethmetaljeff for your help.

I have one last question, how to get ride off the second button “Login With” ?
When I click it, it does nothing just refreshes the page

Screenshot 2024-06-06 at 12.57.46 PM

You probably have an extra socialite config in there. Via cli, make sure there’s only one in the list:

[23:52:45] [[email protected]:~]$ lnms config:get auth.socialite.configs
{
“okta”: {
“client_id”: “xxxxx”,
“client_secret”: “xxxxx”,
“base_url”: “https://xxxx.okta.com”,
“listener”: “\SocialiteProviders\Okta\OktaExtendSocialite”
}
}
[23:53:08] [[email protected]:~]$

We should add your image to Oauth/SAML support - LibreNMS Docs ? to help make things clearer?

I can submit a PR if you are ok for me to submit the image?

@dethmetaljeff

librenms:/opt/librenms# lnms config:get auth.socialite.configs
{
    "": [],
    "okta": {
        "client_id": "0oa74kqs6sqd2La8n0k6",
        "client_secret": "_hh5ufJAodChEi53Lz-_FEbNS0P6txUImJy2RrcUXXKN1kveOA2x7D90GcUx0zC3",
        "base_url": "https://paloaltonetworksgov.okta.mil",
        "listener": "\\SocialiteProviders\\Okta\\OktaExtendSocialite"
    }
}
librenms:/opt/librenms#

I only see one config but also noticed there’s an empty list that you don’t have in your snippet

@faleais that empty list is the problem. You need to remove it.

lnms config:set auth.socialite.configs.""

it’ll ask you if you want to forget the config, say yes.

1 Like

Go for it. Probably also worth putting a short guide in the section that mentions that okta needs to be configured to send the groups claim.

that did it, but I used be able to use admin local account in addition to authenticating through Okta but not anymore, is this expected ?

is there any way to use local account in the event we cannot authenticate through Okta ?

Yes, just leave the Authorization method set to “MySQL” or w/e your default was before enabling socialite. Then both will work. You’d use the grey login button for the local account.

now when I navigate to the URL/login, it forces me to authenticate to Okta. It’s not showing the option to enter username and password anymore.

I just turned socialite off redirect and now I can use local account

Cool, I was going to suggest that. Glad you got it sorted.

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