Open Closed

Implementing Okta Single Sign On #8791


User avatar
0
tahmad created
  • ABP Framework version: v7.4.5

  • UI Type: Angular

  • Database System: EF Core (SQL Serve)

  • Tiered (for MVC) or Auth Server Separated (for Angular): yes/

  • Implemting SSO
    I have implemented Okta SSO and obtained the ID Token from Okta. I have also installed the following packages:

@okta/okta-angular (v6.4.0)
@okta/okta-auth-js (v7.10.1)

Authentication is working as expected, as confirmed by the following check:

this.oktaAuth.isAuthenticated().then(async (authStatus) => {
    if (authStatus) {
        // Authenticated successfully
    }
});

Additionally, I can successfully invoke APIs using this authentication setup but not check authorization with different role:

context.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = "MultipleAuthSchemes";
    options.DefaultChallengeScheme = "MultipleAuthSchemes";
})
.AddPolicyScheme("MultipleAuthSchemes", JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.ForwardDefaultSelector = context =>
    {
        string? authorization = context.Request.Headers["Authorization"];
        if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
        {
            var token = authorization.Substring("Bearer ".Length).Trim();
            return token.Contains("okta") ? "okta_jwt_schema" : JwtBearerDefaults.AuthenticationScheme;
        }
        return JwtBearerDefaults.AuthenticationScheme;
    };
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.Authority = configuration["AuthServer:Authority"];
    options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
    options.Audience = "Project42";
})
.AddJwtBearer("okta_jwt_schema", options =>
{
    options.Authority = configuration["Okta:Authority"];
    options.RequireHttpsMetadata = Convert.ToBoolean(configuration["Okta:RequireHttpsMetadata"]);
    options.Audience = "api://default";
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ValidIssuer = "https://dev-96317405.okta.com/oauth2/default",
        ValidAudience = "api://default",
        ValidateLifetime = true
    };
});

Now, I have route guards set up as follows:

{
    path: '',
    pathMatch: 'full',
    component: DashboardComponent,
    canActivate: [AuthGuard, PermissionGuard, RoleGuard],
}

I believe these guards require the ABP token instead of the Okta token. How can I properly pass authentication to AuthGuard and PermissionGuard while ensuring authorization in the system using Okta?

Can I do something like if authenticated then logged in with the user by matching the email but I don't know the password
I have this method
this.authService
.login({ username, password, rememberMe })

if I can login into the system without password or similar method in backend then I believe i can login the user with proper abp login and can just authenticate with okta.


44 Answer(s)
  • User Avatar
    0
    tahmad created

    Hi
    You are right I checked with ABP login and i have multiple granted permissions
    image.png

    please let me know, what I need to do, thanks.

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    Hello, yes you are right. You will need to handle this part according to the library you use.

    The permission guard only checks the required permissions that you give for the route. However, dashboard component checks for MyProjectName.Dashboard.Host and MyProjectName.Dashboard.Tenant permissions exceptionally.

    Here is the reference for the check: https://github.com/abpframework/abp/blob/2201ee1c349da9dff2a0a333434e660f436b0851/npm/ng-packs/packages/core/src/lib/guards/permission.guard.ts#L47

  • User Avatar
    0
    tahmad created

    Hi sumeyye,

    I'm using okta-angular library but what I need to do to get response from https://localhost:44345/api/abp/application-configuration?includeLocalizationResources=false like the below ss
    image.png

    also this

    also I have seen this
    because this token is of okta open id
    image.png

    what i need to do that this method give me result true this.oAuthService.hasValidAccessToken()

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    I am assuming that oAuthService is from angular-oauth2-oidc library. So, you should be able to use this as you have mentioned at the beginning

        this.oktaAuth.isAuthenticated().then(async (authStatus) => {
            if (authStatus) {
                // Authenticated successfully
            }
        });
    
  • User Avatar
    0
    tahmad created

    I configured oAuthConfig in environment.ts

    oAuthConfig: {
    issuer: 'https://localhost:44345',
    redirectUri: baseUrl,
    clientId: 'Project42_App',
    scope: 'offline_access Project42',
    },

    and for the okta login

    const authConfig = {
    issuer: 'https://dev-******.okta.com/oauth2/default',
    clientId: '----',
    redirectUri: window.location.origin + '/login/callback',
    scopes: ['openid', 'profile', 'email'],
    pkce: true,
    }

    I believe due to two different configurations this.oAuthService.hasValidAccessToken() is resulting false in case of okta.
    so what you suggest in case of okta

    I'm thinking you are saying like this below

    async hasLoggedIn(): Promise<boolean> {
    const authStatus = await this.oktaAuth.isAuthenticated();
    return authStatus || this.oAuthService.hasValidAccessToken();
    }

    this.authService.hasLoggedIn().then(isLoggedIn => {

    });

  • User Avatar
    0
    tahmad created

    also I need this as well

    I'm using okta-angular library but what I need to do to get response from https://localhost:44345/api/abp/application-configuration?includeLocalizationResources=false like the below ss
    image.png

    in this query what I believe is the difference is in case of okta I'm sending okta token else abp access token in bearer auth

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    I'm thinking you are saying like this below

    async hasLoggedIn(): Promise<boolean> {
    const authStatus = await this.oktaAuth.isAuthenticated();
    return authStatus || this.oAuthService.hasValidAccessToken();
    }

    this.authService.hasLoggedIn().then(isLoggedIn => {

    });

    Yes, this is what I mean.

    For the granted policies response that you shared, the okta library should not cause a problem. However, I did not see <MyProjectName>.Dashboard.Host and <MyProjectName>.Dashboard.Tenant permissions that is checked by the guard. Is this right?

  • User Avatar
    0
    tahmad created

    I believe I don't have such conditions <MyProjectName>.Dashboard.Host and <MyProjectName>.Dashboard.Tenant

    also I need this as well

    I'm using okta-angular library but what I need to do to get response from https://localhost:44345/api/abp/application-configuration?includeLocalizationResources=false like the below ss
    image.png

    in this query what I believe is the difference is in case of okta I'm sending okta token else abp access token in bearer auth

  • User Avatar
    0
    tahmad created

    AuthGuard prevent unauthorized users. so it will also I believe used the same like this.oAuthService.hasValidAccessToken() was giving me false response.
    image.png

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    The authGuard also utilizes the angular-oauth2-oidc library, as I mentioned earlier. You can refer to the implementation here: https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts

    Since authGuard follows the logic of this library, you may need to adjust or replace it based on your specific authentication flow.

    Regarding permissionGuard, it should not cause any issues. Could you verify your logic and let me know if the issue persists?

  • User Avatar
    0
    tahmad created

    Hi
    I'm going to write this custom guard, correct me if i'm wrong, thanks.

    import { Injectable } from '@angular/core';
    import {
    CanActivate,
    ActivatedRouteSnapshot,
    RouterStateSnapshot,
    Router,
    } from '@angular/router';
    import { Observable } from 'rxjs';
    import { OktaAuthService } from './okta-auth.service'; // Your OktaAuthService
    import { OAuthService } from 'angular-oauth2-oidc'; // ABP's OAuthService

    @Injectable({
    providedIn: 'root',
    })
    export class CustomAuthGuard implements CanActivate {
    constructor(
    private oktaAuthService: OktaAuthService,
    private oAuthService: OAuthService,
    private router: Router
    ) {}

    canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ): Observable<boolean> | Promise<boolean> | boolean {
    return this.checkAuthentication(state.url);
    }

    private async checkAuthentication(returnUrl: string): Promise<boolean> {
    const isOktaAuthenticated = await this.oktaAuthService.isAuthenticated();
    const hasValidAccessToken = this.oAuthService.hasValidAccessToken();

    if (isOktaAuthenticated || hasValidAccessToken) {
      return true;
    } else {
      // Redirect to your custom login page or handle unauthenticated access
      this.router.navigate(['/login'], { queryParams: { returnUrl } });
      return false;
    }
    

    }
    }

  • User Avatar
    0
    tahmad created

    I'm using abp-nav-items
    in the abp-nav-items I have the logout button
    so now do I need to write custom component for SSO to logout?

    because I need to add logout logic in this button
    image.png

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    Hi
    I'm going to write this custom guard, correct me if i'm wrong, thanks.

    import { Injectable } from '@angular/core';
    import {
    CanActivate,
    ActivatedRouteSnapshot,
    RouterStateSnapshot,
    Router,
    } from '@angular/router';
    import { Observable } from 'rxjs';
    import { OktaAuthService } from './okta-auth.service'; // Your OktaAuthService
    import { OAuthService } from 'angular-oauth2-oidc'; // ABP's OAuthService

    @Injectable({
    providedIn: 'root',
    })
    export class CustomAuthGuard implements CanActivate {
    constructor(
    private oktaAuthService: OktaAuthService,
    private oAuthService: OAuthService,
    private router: Router
    ) {}

    canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ): Observable<boolean> | Promise<boolean> | boolean {
    return this.checkAuthentication(state.url);
    }

    private async checkAuthentication(returnUrl: string): Promise<boolean> {
    const isOktaAuthenticated = await this.oktaAuthService.isAuthenticated();
    const hasValidAccessToken = this.oAuthService.hasValidAccessToken();

    if (isOktaAuthenticated || hasValidAccessToken) { 
      return true; 
    } else { 
      // Redirect to your custom login page or handle unauthenticated access 
      this.router.navigate(['/login'], { queryParams: { returnUrl } }); 
      return false; 
    } 
    

    }
    }

    Hello again, your implementation is correct, but using both OktaAuthService and OAuthService might be redundant unless your app requires multiple authentication providers (e.g., Okta for some users and OAuth2 for others).

    If both services are needed, consider abstracting authentication logic into a single service to simplify maintenance. Otherwise, removing the unnecessary check can reduce complexity.

  • User Avatar
    0
    tahmad created

    Hi
    I need to implement users can use okta and OAuth2 both.
    correct me if I'm wrong, thanks.

    import { Injectable } from '@angular/core';
    import { OktaAuthService } from './okta-auth.service'; // Your existing OktaAuthService
    import { OAuthService } from 'angular-oauth2-oidc'; // OAuthService from angular-oauth2-oidc

    to simplify logic I created Service for authentication logic

    @Injectable({
    providedIn: 'root',
    })
    export class UnifiedAuthService {
    constructor(
    private oktaAuthService: OktaAuthService,
    private oAuthService: OAuthService
    ) {}

    async isAuthenticated(): Promise<boolean> {
    const isOktaAuthenticated = await this.oktaAuthService.isAuthenticated();
    const hasValidAccessToken = this.oAuthService.hasValidAccessToken();
    return isOktaAuthenticated || hasValidAccessToken;
    }
    }

    here is the CustomAuthGuard

    import { Injectable } from '@angular/core';
    import {
    CanActivate,
    ActivatedRouteSnapshot,
    RouterStateSnapshot,
    Router,
    } from '@angular/router';
    import { UnifiedAuthService } from './unified-auth.service'; // The unified authentication service

    @Injectable({
    providedIn: 'root',
    })
    export class CustomAuthGuard implements CanActivate {
    constructor(
    private unifiedAuthService: UnifiedAuthService,
    private router: Router
    ) {}

    async canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ): Promise<boolean> {
    const isAuthenticated = await this.unifiedAuthService.isAuthenticated();
    if (isAuthenticated) {
    return true;
    } else {
    // Redirect to your custom login page or handle unauthenticated access
    this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
    return false;
    }
    }
    }

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    I'm using abp-nav-items
    in the abp-nav-items I have the logout button
    so now do I need to write custom component for SSO to logout?

    because I need to add logout logic in this button
    image.png

    Logout process should also require another custom logic. Here is how you can manage that part:

    import { UserMenuService } from '@abp/ng.theme.shared';
    import { eUserMenuItems } from '@volosoft/abp.ng.theme.lepton-x';
    import { APP_INITIALIZER, inject, NgModule } from '@angular/core';
    
    @NgModule({
      ...
      providers: [
        ...
        { provide: APP_INITIALIZER, useFactory: userMenuFactory },
        ...
      ],
      bootstrap: [AppComponent],
    })
    export class AppModule {}
    
    function userMenuFactory() {
      const userMenu = inject(UserMenuService);
    
      userMenu.patchItem(eUserMenuItems.Logout, {
        action: () => {
          console.log('Custom logout action');
        },
      });
    }
    
    
  • User Avatar
    0
    tahmad created

    image.png

    I added this

    image.png

    I'm trying to fix.

  • User Avatar
    0
    sumeyye.kurtulus created
    Support Team Angular Expert

    Could you also try replacing the component like this https://abp.io/docs/latest/framework/ui/angular/component-replacement#how-to-replace-a-component

    import { ReplaceableComponentsService } from '@abp/ng.core';
    import { Component, inject } from '@angular/core';
    import { eThemeLeptonXComponents } from '@volosoft/abp.ng.theme.lepton-x';
    import { MySettingsComponent } from './my-settings/my-settings.component';
    
    @Component({
      selector: 'app-root',
      template: `
        &lt;abp-loader-bar&gt;&lt;/abp-loader-bar&gt;
        &lt;abp-dynamic-layout&gt;&lt;/abp-dynamic-layout&gt;
        &lt;abp-gdpr-cookie-consent&gt;&lt;/abp-gdpr-cookie-consent&gt;
      `,
    })
    export class AppComponent {
      private replaceComponent = inject(ReplaceableComponentsService);
      constructor() {
        this.replaceComponent.add({
          component: MySettingsComponent,
          key: eThemeLeptonXComponents.Toolbar,
        });
      }
    }
    
  • User Avatar
    0
    tahmad created

    Hi,

    I'll get back to you soon. I'm currently working on setting things up with Okta, and once that's done, I'll have a few questions for you. Thanks!

    In the meantime, I do have one question—my application supports multiple grant types for OAuth login, but Okta has fewer grant types configured. Could this potentially impact the application flow? Everything seems to be working fine for now.

    image.png

  • User Avatar
    0
    berkansasmaz created
    Support Team .NET Developer

    It shouldn't, but you can let us know if there is a problem. We are waiting to hear from you for now.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.3.0-preview. Updated on June 03, 2025, 11:25