-
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)
-
0
Hi
You are right I checked with ABP login and i have multiple granted permissions
please let me know, what I need to do, thanks.
-
0
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
andMyProjectName.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
-
0
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
also this
also I have seen this
because this token is of okta open id
what i need to do that this method give me result true this.oAuthService.hasValidAccessToken()
-
0
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 beginningthis.oktaAuth.isAuthenticated().then(async (authStatus) => { if (authStatus) { // Authenticated successfully } });
-
0
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 oktaI'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 => {
});
-
0
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
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
-
0
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? -
0
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
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
-
0
AuthGuard prevent unauthorized users. so it will also I believe used the same like this.oAuthService.hasValidAccessToken() was giving me false response.
-
0
The
authGuard
also utilizes theangular-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.tsSince
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? -
0
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; }
}
} -
0
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
-
0
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.
-
0
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-oidcto 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;
}
}
} -
0
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
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'); }, }); }
-
0
I added this
I'm trying to fix.
-
0
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: ` <abp-loader-bar></abp-loader-bar> <abp-dynamic-layout></abp-dynamic-layout> <abp-gdpr-cookie-consent></abp-gdpr-cookie-consent> `, }) export class AppComponent { private replaceComponent = inject(ReplaceableComponentsService); constructor() { this.replaceComponent.add({ component: MySettingsComponent, key: eThemeLeptonXComponents.Toolbar, }); } }
-
0
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.
-
0
It shouldn't, but you can let us know if there is a problem. We are waiting to hear from you for now.