Azure Entra ID agent authorization or any OpenID

Moderator: crythias

Post Reply
nilskm
Znuny newbie
Posts: 23
Joined: 29 Sep 2010, 09:50
Znuny Version: 6.0.30
Real Name: Nils

Azure Entra ID agent authorization or any OpenID

Post by nilskm »

I've made this work, so I thought I'd share it here. If someone want to improve upon it, please do. Authorization is done through OpenID and Azure Entra ID in this case. But it can be used with any auth that supports OpenID.

In short, do this:
  1. Install mod_auth_openidc and configure it
  2. Authorize with this in Config.pm:

    Code: Select all

    $Self->{'AuthModule'} = 'Kernel::System::Auth::HTTPBasicAuth';
Here's a step by step the way I made it work. I used fedora 41, and I can also share that I tried it on an old CentOS 7.9 where it didn't work. Don't know why it didn't work, but I gave up finally
  1. About the apache module, here: https://github.com/OpenIDC/mod_auth_ope ... (Azure-AD).
  2. The module is installed simple with "dnf install mod_auth_openidc" as long as you're on Fedora or have EPEL installed.
  3. Instructions to install/configure it with Entra ID. Follow the instructions here: https://github.com/OpenIDC/mod_auth_ope ... (Azure-AD).
  4. This is how my apache configuration ended up. Put it in a file that's read by apache, e.g in /etc/httpd/conf.d/znuny_oid.conf . It can be improved upon with virtual hosts etc. Maybe someone want to improve upon it with some details?

    Code: Select all

    OIDCProviderMetadataURL https://login.microsoftonline.com//<TENANT_ID>/v2.0/.well-known/openid-configuration
    OIDCClientID <client-id>
    OIDCClientSecret <client-secret>
    OIDCRedirectURI https://www.yourdomain.com/redirect_uri
    OIDCCryptoPassphrase "use-a-random-long-string-here"
    
    OIDCRemoteUserClaim email
    RequestHeader set REMOTE_USER expr=%{REMOTE_USER}
    OIDCPassClaimsAs environment
    OIDCClaimPrefix OIDC_
    
    <Location />
        AuthType openid-connect
        Require valid-user
    </Location>
    
  5. Add this in Config.pm for authorization

    Code: Select all

    $Self->{'AuthModule'} = 'Kernel::System::Auth::HTTPBasicAuth';
  6. Added new sync module. I couldn't find an existing one so I, well actually ChatGPT, made one for me. It has a flaw, it can't handle foreign letters, e.g Swedish åäö. But I didn't have time to fix that, maybe someone else want to? Put it in e.g. opt/znuny/Kernel/System/Auth/Sync/OID.pm and make sure it's readable by apache: chown znuny:apache + chmod 644 for the file.

    Code: Select all

    package Kernel::System::Auth::Sync::OIDC;
    
    use strict;
    use warnings;
    use Encode ();
    
    use Kernel::System::User;
    #use Data::Dumper;
    #warn "ENV: ".Dumper(\%ENV);
    
    sub new {
        my ( $Type, %Param ) = @_;
        return bless \%Param, $Type;
    }
    
    sub Sync {
        my ( $Self, %Param ) = @_;
    
        my $UserObject = Kernel::System::User->new( %{$Self} );
    
        my $Login = $Param{User};  # use full email
    
        my $UserID = $UserObject->UserLookup( UserLogin => $Login );
        return $UserID if $UserID;
    
        my $First = $ENV{OIDC_given_name}     || 'First';
        my $Last  = $ENV{OIDC_family_name}    || 'Last';
        my $Email = $ENV{OIDC_email}          || $Login;
    
        # Ensure UTF-8 encoding
        $Self->_ConvertTo( \$First );
        $Self->_ConvertTo( \$Last );
        $Self->_ConvertTo( \$Email );
        $Self->_ConvertTo( \$Login );
    warn "DECODED LAST: " . Encode::encode('UTF-8', $Last) . "\n";
    
        $UserID = $UserObject->UserAdd(
            UserFirstname => $First,
            UserLastname  => $Last,
            UserLogin     => $Login,
            UserEmail     => $Email,
            ValidID       => 1,
            GroupIDs      => [ 1 ],
            ChangeUserID  => 1,
        );
    
        return $UserID;
    }
    sub _ConvertTo {
        my ( $Self, $RefString ) = @_;
        return if !defined $$RefString;
    
        # Only decode if it's a byte string
        #if ( !Encode::is_utf8( $$RefString ) && $$RefString =~ /[^\x00-\x7F]/ ) {
            $$RefString = Encode::decode( 'UTF-8', $$RefString );
            #}
    
        return;
    }
    
    sub _ConvertTo1 {
        my ( $Self, $RefString ) = @_;
        return if !$$RefString;
        return if Encode::is_utf8( $$RefString );
        $$RefString = Encode::decode( 'utf-8', $$RefString );
    }
    
    sub _ConvertFrom {
        my ( $Self, $RefString ) = @_;
        return if !$$RefString;
        return if !Encode::is_utf8( $$RefString );
        $$RefString = Encode::encode( 'utf-8', $$RefString );
    }
    1;
    Example of sync module further down.
  7. To use the sync module, add this to Config.pm. I'm not sure how or why it works with LDAP there, but maybe someone else can explain that?

    Code: Select all

    $Self->{'AuthSyncModule'} = 'Kernel::System::Auth::Sync::OIDC';
    $Self->{'AuthSyncModule::LDAP::UserSyncMap'} = {
        UserFirstname => 'given_name',
        UserLastname  => 'family_name',
        UserEmail     => 'email',
    };
    
  8. Problems that occurred. For me users were earlier synced with an on premise windows domain server. An that meant that user name therefore mostly were something like firstname.lastname. But in the configuration above the user name will be your email address. I solved it simple by manually changing user name to email for each user. Probably a simple SQL UPDATE would do it too. Maybe something similiar to this would work too:

    Code: Select all

    OIDCRemoteUserClaim email
    OIDCRemoteUserTransformRegex ^([^@]+)@.*$ 
Well, that's it, hope I didn't miss anything. This could, of course, be used to authorize Customers too, but it wasn't relevant in my case.
Post Reply