Target architecture
- Identity provider: Okta (OIDC/OAuth2).
- Directory / SSO source: On‑prem Windows AD, integrated with Okta.
- Client: Angular SPA (OIDC Code + PKCE).
- API: ASP.NET Core Web API secured with JWT.
- Hosting: Angular + API on AWS EC2 (or Angular on S3/CloudFront).
- Flow: Angular → Okta → AD → tokens → Angular → API → Okta validation.
Set up Okta and connect Windows AD
- Create/verify Okta org.
- Install Okta AD Agent → connect to Okta → sync OUs/groups.
- Enable Desktop SSO / IWA or seamless SSO.
Create Okta apps for Angular SPA and C# API
Angular SPA
- OIDC SPA app → Authorization Code + PKCE.
- Redirect URIs for login/logout.
- Assign AD users/groups.
- Trusted Origins for CORS.
API Resource Server
- Create API app → define audience.
- Create scopes (api.read, api.write).
- Authorize SPA to request scopes.
Wire up Angular with Okta
- Install Okta SDKs.
- Configure OktaAuth + OKTA_CONFIG.
- Use OktaAuthGuard for protected routes.
- Use HTTP interceptor for Bearer tokens.
Secure the C# API
- Add JwtBearer package.
- Configure Authority + Audience.
- Use
[Authorize] on controllers.
Deploy on AWS
- Host Angular on EC2 or S3/CloudFront.
- Host API on EC2 (Linux or Windows).
- Ensure HTTPS + correct redirect URIs.
End‑to‑end SSO flow
- User opens Angular.
- Guard redirects to Okta.
- Okta authenticates via AD.
- Okta returns code → Angular exchanges for tokens.
- Angular calls API with Bearer token.
- API validates JWT via Okta.
Code
Angular – Install Packages
npm install @okta/okta-angular @okta/okta-auth-js
Angular – okta-config.ts
export const oktaConfig = {
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{yourSpaClientId}',
redirectUri: window.location.origin + '/callback',
scopes: ['openid', 'profile', 'email', 'api.read'],
pkce: true,
};
Angular – AppModule
import { OktaAuth } from '@okta/okta-auth-js';
import { OktaAuthModule, OKTA_CONFIG } from '@okta/okta-angular';
import { oktaConfig } from './okta-config';
const oktaAuth = new OktaAuth(oktaConfig);
@NgModule({
imports: [BrowserModule, AppRoutingModule, OktaAuthModule],
providers: [{ provide: OKTA_CONFIG, useValue: { oktaAuth } }],
bootstrap: [AppComponent]
})
export class AppModule {}
Angular – Routes
const routes: Routes = [
{ path: 'callback', component: OktaCallbackComponent },
{ path: 'secure', component: SecureComponent, canActivate: [OktaAuthGuard] },
{ path: '**', redirectTo: 'home' }
];
Angular – HTTP Interceptor
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private oktaAuth: OktaAuth) {}
async intercept(req: HttpRequest, next: HttpHandler) {
const accessToken = await this.oktaAuth.getAccessToken();
if (accessToken && req.url.startsWith('https://your-api-host')) {
req = req.clone({
setHeaders: { Authorization: `Bearer ${accessToken}` }
});
}
return next.handle(req);
}
}
C# – Add JWT Package
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
C# – Program.cs
var builder = WebApplication.CreateBuilder(args);
var oktaDomain = builder.Configuration["Okta:Domain"];
var audience = builder.Configuration["Okta:Audience"];
builder.Services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = $"{oktaDomain}/oauth2/default";
options.Audience = audience;
});
builder.Services.AddAuthorization();
builder.Services.AddControllers();
var app = builder.Build();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
C# – Secure Controller
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class ValuesController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok(new { message = "Secure data" });
}