简体   繁体   中英

401 unauthorized error for Okta+Springboot+angular

I am working on a springboot+angular app with Okta authentication but I am getting 401 Unauthorized error. Following is my code on the front end side:

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { OktaCallbackComponent } from '@okta/okta-angular';
import { OktaAuthGuard } from './app.guard';
import { UserDetailsComponent } from './components/user-details/user-details.component';

const routes: Routes = [
 { path: 'home', canActivate: [OktaAuthGuard], component: UserDetailsComponent },
 { path: 'def', component: OktaCallbackComponent },
 { path: '**', redirectTo: '', pathMatch: 'full' },
];

@NgModule({
 imports: [RouterModule.forRoot(routes)],
 exports: [RouterModule]
})
export class AppRoutingModule { }

app.component.ts

import { Component } from '@angular/core';
import { OktaAuthService } from '@okta/okta-angular';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'users';
isAuthenticated: boolean;
constructor(public oktaAuth: OktaAuthService) {
    // subscribe to authentication state changes
    this.oktaAuth.$authenticationState.subscribe(
    (isAuthenticated: boolean)  => this.isAuthenticated = isAuthenticated
    );
}
async ngOnInit() {
    // get authentication state for immediate use
    this.isAuthenticated = await this.oktaAuth.isAuthenticated();
}
async login() {
    await this.oktaAuth.signInWithRedirect({
    originalUri: '/users'
    });
}
async logout() {
    await this.oktaAuth.signOut();
}
}

app.guard.ts

import { OktaAuthService } from '@okta/okta-angular';
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable({
providedIn: 'root'
})
export class OktaAuthGuard implements CanActivate {
oktaAuth;
authenticated;
constructor(private okta: OktaAuthService, private router: Router) {
    this.oktaAuth = okta;
}

async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    this.authenticated = await this.okta.isAuthenticated();
    console.log('can activate?', this.authenticated);
    if (this.authenticated) { return true; }
    // Redirect to login flow.
    this.okta.signInWithRedirect();
    return false;
}
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
//import { OktaAuthModule, OktaCallbackComponent } from '@okta/okta-angular';
import {OktaAuthModule, OKTA_CONFIG} from '@okta/okta-angular';
import { UserDetailsComponent } from './components/user-details/user-details.component';
import { ReactiveFormsModule } from '@angular/forms';
import { OktaAuthGuard } from './app.guard';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { XSRFTokenInterceptor } from './xsrf-token-interceptor';
import { AuthInterceptor } from './auth.interceptor';

const oktaConfig = {
issuer: 'https://dev-xxxxxxxx.okta.com/oauth2/default',
redirectUri: window.location.origin + '/users',
clientId: 'xxxxxxxxxxxxxxx',
pkce: true
};

@NgModule({
declarations: [
    AppComponent,
    UserDetailsComponent
],
imports: [
    BrowserModule,
    OktaAuthModule,
    AppRoutingModule,
    HttpClientModule
],
providers: [
    OktaAuthGuard,
    {provide: HTTP_INTERCEPTORS, useClass : XSRFTokenInterceptor, multi: true},
    { provide: OKTA_CONFIG, useValue: oktaConfig },
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},],
bootstrap: [AppComponent]
})
export class AppModule { }

auth.interceptor.ts

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable, from } from 'rxjs';
import { OktaAuthService } from '@okta/okta-angular';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

constructor(private oktaAuth: OktaAuthService) {
}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return from(this.handleAccess(request, next));
}

private async handleAccess(request: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {

    const accessToken = await this.oktaAuth.getAccessToken();

    if (accessToken) {
    console.log('token: ' + accessToken);
    request = request.clone({
        setHeaders: {
        Authorization: 'Bearer ' + accessToken
        }
    });
    }
    return next.handle(request).toPromise();
}
}

user.service.ts

 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 import { environment } from 'src/environments/environment';
 import { map } from 'rxjs/operators';
 import { HandleError, HttpErrorHandler } from './http-error-handler.service';
 import { catchError } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
})
export class UserService {
private handleError: HandleError;
contactsUrl = environment.contactAPI;
constructor(private http: HttpClient,
    httpErrorHandler: HttpErrorHandler) {
    this.handleError = httpErrorHandler.createHandleError('HeroesService');
}

getUsers() {
 return this.http.get<any>(this.contactsUrl)
    .pipe(map(info => {
        console.log("info: "+info);            
        return info;
    }));

}}

xsrf-token-interceptor.ts

 import { Injectable } from '@angular/core';
 import {HttpEvent, HttpRequest, HttpHandler,HttpInterceptor, HttpErrorResponse, 
  HttpXsrfTokenExtractor} from '@angular/common/http';
 import { Observable } from 'rxjs';

@Injectable()
export class XSRFTokenInterceptor implements HttpInterceptor {

constructor(private tokenExtractor: HttpXsrfTokenExtractor) {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
     return next.handle(req);
   }
}

At the backend:

SecurityDevConfiguartion.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpMethod;
import 
org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import java.util.Arrays;


@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)

public class SecurityDevConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
    System.out.println("configure");
    http.cors().and().csrf().disable();
  try {
        http.antMatcher("/**")
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated();
    }catch(Exception e){
        e.printStackTrace();
    }
    http.oauth2ResourceServer();
}

@Bean
CorsConfiguration corsConfiguration() {
    CorsConfiguration corsConfiguration  = new CorsConfiguration();
    corsConfiguration.setAllowCredentials(true);
    corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
    corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Access-Control, Allow-Origin", "Content-Type", "Accept", "Authorization", "Origin, Accept", "X-Requested-With", "Access-Control-Request-Method", "Access-Control-Request-Header" )); 
    corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization", "Access-Control-Request-Allow-Origin", "Access-Control-Allow-Credentials"));
    corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
    
    return corsConfiguration ;
}
}

UsersController.java

import com.example.users.dao.usersDAO;
import com.example.users.model.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class UsersController {

@Autowired
usersDAO usersDAO;

@RequestMapping(value = "/getUsers", method = RequestMethod.GET)
@ResponseBody
public List getUsers(@AuthenticationPrincipal Principal userInfo) throws Exception {
    System.out.println("userInfo: "+userInfo);
    List<Users> users = usersDAO.findAll();
    List<String> l = new ArrayList();
    for(Iterator i=Users.iterator();i.hasNext();){
        Users u = (Users)(i.next());
        l.add(u.getName());
    }
    return l;
}
}

application.properties has client-id and issuer mentioned. 'http//localhost:4200' is mentioned in trusted origins in Okta security.

What am I missing/doing wrong? Any help is appreciated.

Your Okta tenant does have API Access Management feature enabled? Access tokens created through this authorization servers can be verified locally as the signing keys for them are returned under /keys endpoint.

This feature is free for developer, but paid in production ones. If you would like to have this feature enabled on your production tenant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM