I have 2 problems.
First one
Basically i have my application divided to Angular frontend and Spring-boot backend. I can login to Auth0 by my angular app, and im getting access token which i keep in localstorage. My goal is to make request to my backend and get resources based on my scopes in my access token. Im sending request by http with bearer token in authorization header. But when im doing these requests by angular im getting 401 unauthorized code in response. Its strange because while im making api calls by soapUI “public” path works and im getting the message.
I have tried to find some answers in the net but i found only solutions for regular web app where login logic is written in spring-boot but in my projects all login logic is in angular with help of auth0. What i need to do? I think the problem is my spring-boot app is not reading these authorization tokens despite i did some configuration.
Its my authconfig class
@Configuration
@EnableWebSecurity
public class AuthConfig extends WebSecurityConfigurerAdapter {
@Value(value = "${auth0.apiAudience}")
private String apiAudience;
@Value(value = "${auth0.issuer}")
private String issuer;
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
configuration.setAllowCredentials(true);
configuration.addAllowedHeader("Authorization");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
JwtWebSecurityConfigurer
.forRS256(apiAudience, issuer)
.configure(http)
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/api/public").permitAll()
.antMatchers(HttpMethod.GET,"/api/private").authenticated();
}
My controller:
@CrossOrigin(origins = "*", allowedHeaders = "*")
@RestController
@RequestMapping("/api")
public class ApiController {
@RequestMapping(value = "/public", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public String publicEndpoint() {
return new JSONObject()
.put("message", "Hello from a public endpoint! You don\'t need to be authenticated to see this.")
.toString();
}
@RequestMapping(value = "/private", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public String privateEndpoint() {
return new JSONObject()
.put("message", "Hello from a private endpoint! You need to be authenticated to see this.")
.toString();
}
@RequestMapping(value = "/private-scoped", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public String privateScopedEndpoint() {
return new JSONObject()
.put("message", "Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.")
.toString();
}
}
my request headers
![image|690x319](upload://bzFeZKtqA8MxC1JBW1L7biaftB2.png)
-------------------------------------------------------------------------------------------------------------------------
My second problem is in my Angular application. Because when im logging using auth0 and after im redirected to my application after succed authentication i need to refresh the page to get my user-profile informations. And because its simple page application refreshing the page is rare. Maybe i need to use some kind of async function?
auth.service
userProfile: any;
public auth0 = new auth0.WebAuth({
clientID: environment.clientID,
domain: environment.domain,
responseType: environment.responseType,
audience: environment.audience,
redirectUri: environment.redirectUri,
scope: environment.requestedScopes
});
constructor(private router: Router) {
}
public getProfile(cb): void {
const accessToken = localStorage.getItem('access_token');
if (!accessToken) {
throw new Error('Access Token must exist to fetch profile');
}
const self = this;
this.auth0.client.userInfo(accessToken, (err, profile) => {
if (profile) {
self.userProfile = profile;
console.log(self.userProfile)
}
cb(err, profile);
});
}
public login() {
this.auth0.authorize();
}
public handleAuthentication(): void {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
window.location.hash = '';
this.setSession(authResult);
this.router.navigate(['/home']);
} else if (err) {
this.router.navigate(['/error']);
console.log(err);
alert('Error: ${err.error}. Check the console for further details.');
}
});
}
private setSession(authResult): void {
const expiresAt = JSON.stringify(authResult.expiresIn * 1000 + new Date().getTime());
const scopes = authResult.scope || environment.requestedScopes || '';
localStorage.setItem('access_token', authResult.accessToken);
localStorage.setItem('id_token', authResult.idToken);
localStorage.setItem('expires_at', expiresAt);
localStorage.setItem('scopes', JSON.stringify(scopes));
}
public logout(): void {
localStorage.removeItem('access_token');
localStorage.removeItem('id_token');
localStorage.removeItem('expires_at');
localStorage.removeItem('scopes');
this.router.navigate(['/']);
}
public isAuthenticated(): boolean {
const expiresAt = JSON.parse(localStorage.getItem('expires_at'));
return new Date().getTime() < expiresAt;
}
public userHasScopes(scopes: Array<string>): boolean {
const grantedScopes = JSON.parse(localStorage.getItem('scopes')).split(',');
return scopes.every(scope => grantedScopes.includes(scope));
}
public renewToken() {
this.auth0.checkSession((err,result) => {
if(!err){
this.setSession(result);
}
})
}
And im calling it in onInit in app compoent
export class AppComponent implements OnInit {
title = 'SchoolGit';
constructor(public auth: AuthService, private postService:
PostServiceService) {
auth.handleAuthentication();
}
ngOnInit() {
this.postService.getMessage();
}
}
And this is my app module.
import { JwtModule } from '@auth0/angular-jwt';
import { AuthService } from './services/auth.service';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NavbarComponent } from './components/navbar/navbar.component';
import { FooterComponent } from './components/footer/footer.component';
import { SubjectComponent } from './components/subject/subject.component';
import { SidemenuComponent } from './components/sidemenu/sidemenu.component';
import { HomeComponent } from './components/home/home.component';
import { ProfileComponent } from './components/profile/profile.component';
import { CallbackComponent } from './components/callback/callback.component';
import { HttpClientModule } from '@angular/common/http';
import { NotfoundComponent } from './components/notfound/notfound.component';
import { CreatePostComponent } from './components/create-post/create-post.component';
export function tokenGetter(){
return localStorage.getItem('access_token');
}
@NgModule({
declarations: [
AppComponent,
NavbarComponent,
FooterComponent,
SidemenuComponent,
SubjectComponent,
HomeComponent,
ProfileComponent,
CallbackComponent,
NotfoundComponent,
CreatePostComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
JwtModule.forRoot({
config: {
tokenGetter: tokenGetter,
whitelistedDomains: ['localhost:8080']
}
})
],
providers: [AuthService],
bootstrap: [AppComponent]
})
export class AppModule { }
And my post-service
export class PostServiceService {
constructor(public http: HttpClient) { }
getMessage() {
return this.http.get(environment.api_endpoint + '/api/public').subscribe(data => console.log(data), err => console.log(err));
}
}
If you need i can give you a link to my repository.