Missing roles when using auth repository

I am using a MicrosoftGraphAuthProvider hooked to Azure AD which is where I assign roles to the user. When I also add an auth repository the roles stop flowing to the Authenticate response. I can see the roles populated on the session but they are not being returned via my authenticate call. Removing the auth repo makes roles flow as expected. I am using a JwtAuthProvider as well. I am also trying to add a plain CredentialsProvider in the mix. The reason I want to get the auth repo to work is to take advantage of some of the built in admin ui. How can I get this working?

1 Like

Hi @mikeg,

How are you calling Authenticate when using the MicrosoftGraphAuthProvider? Could you provide some more details of you setup? Eg, your AuthFeature plugin configuration? Thanks

We are using an React SPA that actually does a window.location.assign(`${process.env.REACT_APP_BASE_URL}auth/microsoftgraph with REACT_APP_BASE_URL being equal to the api.

The AuthFeature is setup like this:

var azureAdOauthSettings = new MultiAppSettingsBuilder()                                                                                                                                                                        
    .AddDictionarySettings(new Dictionary<string, string>                                                                                                                                                                       
        {"oauth.microsoftgraph.AppSecret", secretsManager.Get("AzureAdClientAppSecret")},                                                                                                                                       
        {"oauth.microsoftgraph.Tenant", $"{settings.AdTenantId}"}                                                                                                                                                               
Plugins.Add(new AuthFeature(() => new CustomUserSession(),                                                                                                                                                                      
    new IAuthProvider[] {                                                                                                                                                                                                       
        new CustomMicrosoftGraphAuthProvider(azureAdOauthSettings, container.Resolve<IWorkstationDetailsRepository>()),                                                                                                         
        new JwtAuthProvider(AppSettings) {                                                                                                                                                                                      
            ExpireTokensIn = TimeSpan.FromHours(1),                                                                                                                                                                             
            ExpireRefreshTokensIn = TimeSpan.FromHours(1),                                                                                                                                                                      
            IncludeJwtInConvertSessionToTokenResponse = true,                                                                                                                                                                   
            UseTokenCookie = true,                                                                                                                                                                                              
            AuthKey = secretsManager.Get("ServiceStack_JwtAuthKey").ToUtf8Bytes(),                                                                                                                                              
            PopulateSessionFilter = (session, payload, req) =>                                                                                                                                                                  
                var workstationDetailsRepository = container.Resolve<IWorkstationDetailsRepository>();                                                                                                                          
                var workstationDetails = workstationDetailsRepository.GetWorkstationDetailsByAdEmail(session.Email).GetAwaiter().GetResult();                                                                                   
                if (workstationDetails != null)                                                                                                                                                                                 
                    ((CustomUserSession)session).HostAddress = workstationDetails.HostAddress;                                                                                                                                  
                    ((CustomUserSession)session).Company = workstationDetails.Company;                                                                                                                                          
                    ((CustomUserSession)session).WorkstationDescription = workstationDetails.WorkstationDescription;                                                                                                            
                    ((CustomUserSession)session).OwnerEmail = workstationDetails.OwnerEmail ?? "None";                                                                                                                          
                    ((CustomUserSession)session).Region = workstationDetails.Region;                                                                                                                                            
                    // ((CustomUserSession) session).Id = $"{session.Email}:{GetRoleArray(session.Roles)}:{session.DisplayName}";                                                                                               
                    // ((CustomUserSession) session).UserAuthId = $"{session.Email}:{GetRoleArray(session.Roles)}:{session.DisplayName}";      was using these to 'persist' session after timeout before we set token to 1 hour 

I am interested as well. I have AzureAD integrated via OpenIDConnect but I can’t access the admin-ui feature because it doesn’t exist in the AuthRepo.

Hi @mikeg,

Are you able to share what CustomMicrosoftGraphAuthProvider is doing?

The issue boils down to the AuthenticateService always pulling roles from an AuthRepository if registered, otherwise falling back to roles in the session.

Azure AD App Roles are not synced with the AuthRepository and there for only exist in the user session.

Using a credentials provider for access to the Admin UI with a user assigned the Admin role via AssignRoles should work no problem, however roles will still be internal to the ServiceStack application itself and not be reflected in any external systems. If you login using microsoftgraph while already logged in to a valid credentials auth user, the application should associate the accounts together allowing you to manage the admin UI while still logging in with the Azure AD account.

This will still only pull roles from the AuthRepository related to the original registered user using the credentials auth provider, but the user will have an entry in the UserAuthDetails table for the microsoftgraph auth provider as well.

Alternatively, you can assign roles to a user that was created when signing into your application using microsoft graph (with an existing user with the Admin role).

Also for features like User Management in the Admin UI, this only manages users within the ServiceStack app. It would not manage users in the registered Azure AD Directory environment.

If you want roles from the Azure AD App Roles to populate into your AuthRepository, that is something you would have to currently manage yourself. One way of doing this is to override the OnAuthenticatedAsync of your CustomMicrosoftGraphAuthProvider and after a base.OnAuthenticatedAsync call, use the populated session to populate/add/delete roles in your AuthRepository. These would however become stale or out of sync if changes to your Azure AD were made for the user, they would have to logout/in to sync or you would have to manage some external sync process as well.

If you could describe more about your use case in regards to using the Admin UI with Azure AD App Roles and users? Eg

  • What features do you want to use in the Admin UI?
  • How do you expect the feature like user management in Admin UI to behave in your use case?
  • If you had multiple Auth providers with custom roles, how would you expect these to interact?

Any details you can share would help use better understand how these features are being received, and how people want to use them.

Hope that helps.

1 Like

@mikeg @bgiromini we have committed a change that is now available on MyGet that enables the MicrosoftGraphAuthProvider to work with the and the OrmLiteAuthRepository to merge roles for the user. This means you should be able to add the OrmLiteAuthRepository to your application and your users will have their rolls populate in your database from your Azure AD Application Roles.

You will then be able to use the User Management UI to manage local Auth Repository roles of those same users. This still keeps the roles separate as described above, but does the synchronizing for you. Adding roles to users in the User Management UI won’t impact your Azure AD Application Roles, it will only save roles locally to your registered OrmLiteAuthRepository and related database.

If you could update your application and try this new feature out to let us know how it works with your own use cases? This will help us target common needs of users that are integrating their ServiceStack apps with Azure AD. Thanks!

1 Like

Will this only work with the OrmLiteAuthRepository or will it work with the DynamoAuthRepository as well? It may be a bit before I can test this as priorities have shifted a bit.

OrmLiteAuthRepository only with roles managed in an external table with UseDistinctRoleTables=true.

1 Like