Angular2 Spring Authentication - spring

I have separate Angular2 Client and Spring hosted servers.
My Angular2 application is able to call rest call of the spring.
But, I am facing few difficulties to do CSRF authentication with Spring.
main.ts:
import { CsrfBaseRequestOptions } from './app/shared';
bootstrap(AppComponent, [
ROUTER_PROVIDERS,
HTTP_PROVIDERS,
...
provide(RequestOptions, { useClass: CsrfBaseRequestOptions })
]);
XhrBaseRequestOptions:
#Injectable()
export class XhrBaseRequestOptions extends BaseRequestOptions {
constructor() {
super();
this.headers.append('X-Requested-With', 'XMLHttpRequest');
}
}
CsrfBaseRequestOptions:
import { Injectable } from '#angular/core';
import { XhrBaseRequestOptions } from './xhr-base-request-options';
#Injectable()
export class CsrfBaseRequestOptions extends XhrBaseRequestOptions {
constructor() {
super();
let csrfToken = this.getCsrfToken('X-CSRF-TOKEN');
if (csrfToken) {
this.headers.append('X-CSRF-TOKEN', csrfToken);
}
}
getCsrfToken(tokenName:string):string {
let tokenNameEQ = tokenName + '=';
let ck = document.cookie;
let ca = ck.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
if (c.indexOf(tokenNameEQ) === 0) return c.substring(tokenNameEQ.length, c.length);
}
return null;
}
}
index.ts
onSubmit(event, username, password) {
this.headers = new Headers();
this.headers.append("Content-Type", 'application/json');
let body = JSON.stringify({username, password});
this.http.post('http://localhost:8080/EEP/test', body, { headers: this.headers })
.subscribe((res) => this.token = res.json())
}
hear Nothing is happening.
THanks in advance if you help me to call the Spring ..

I think that you could use the Automatic XSRF handling of Angular2 available from RC2. This is done under the hood by the XSRFHandler class and don't need to add explicitly the cookie to the request.
See these links:
http://5thingsangular.github.io/2016/05/30/issue-6.html
https://github.com/angular/angular/pull/8898

Related

Problem with JWT verification in Next.js and Spring Boot

I am trying to make protected routes in Next.js using middleware:
import { type NextRequest, NextResponse } from 'next/server';
import { verifyAuth } from '#helpers/auth/verifyJWT';
export const config = {
matcher: ['/dash', '/auth/:path*'],
};
export async function middleware(req: NextRequest) {
const verifiedToken = await verifyAuth(req).catch((err) => {
console.error(err.message);
});
if (!verifiedToken) {
if (req.nextUrl.pathname.startsWith('/dash')) {
return NextResponse.redirect(new URL('/auth/login', req.url));
} else {
return NextResponse.next();
}
} else {
if (req.nextUrl.pathname.startsWith('/auth')) {
return NextResponse.redirect(new URL('/dash', req.url));
} else {
return NextResponse.next();
}
}
}
Here is verifyAuth method:
import type { NextRequest } from 'next/server';
import { jwtVerify } from 'jose';
import { USER_TOKEN, getJwtSecretKey } from '../constatns';
interface UserJwtPayload {
jti: string;
iat: number;
}
export class AuthError extends Error {}
export async function verifyAuth(req: NextRequest) {
const token = req.cookies.get(USER_TOKEN)?.value;
if (!token) throw new AuthError('Missing user token ');
try {
const verified = await jwtVerify(
token,
new TextEncoder().encode(getJwtSecretKey()),
);
return verified.payload as UserJwtPayload;
} catch (err) {
throw new AuthError('Your token has expired.');
}
}
Problem is that I always get the message: Token is expired.
I am using Spring Boot for backend part, here is my SpringSecurity part for signing token:
public String generateToken(
Map<String, Object> extraClaims,
UserDetails userDetails
) {
return Jwts
.builder()
.setClaims(extraClaims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 24))
.signWith(getSignInKey(), SignatureAlgorithm.HS256)
.compact();
}
private Key getSignInKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
So I tried many libraries to code/encode my secret key in the Nextjs app but same error.
I am not sure how to do it.
Is there any other solution?

Angular, error 500 after sending the request in the header

I have a hard time passing the right angular request to the header. This is my service:
import { Injectable } from '#angular/core';
import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpHeaders }
from '#angular/common/http';
import { Utente } from '../model/Utente ';
import { Prodotto } from '../model/Prodotto ';
import { OktaAuthService } from '#okta/okta-angular';
import { Observable, from } from 'rxjs';
import { Carrello } from '../model/Carrello ';
import { userInfo } from 'node:os';
import { getLocaleCurrencyCode } from '#angular/common';
const headers = new HttpHeaders().set('Accept', 'application/json');
#Injectable({
providedIn: 'root'
})
export class HttpClientService {
constructor(
private httpClient:HttpClient, private oktaAuth:OktaAuthService ) {}
getCarr(){
return this.httpClient.get<Carrello[]>('http://localhost:8080/prodotti/utente/vedicarrelloo', {headers} );
}
}
This is my spring method:
#Transactional(readOnly = true)
public List<Carrello> getCarrello(#AuthenticationPrincipal OidcUser utente){
Utente u= utenteRepository.findByEmail(utente.getEmail());
return carrelloRepository.findByUtente(u);
}
In console I get this error (error 500):
https://i.stack.imgur.com/BiONS.png
this error corresponds in my console to "java.lang.NullPointerException: null.
But if I access localhost: 8080, I can see the answer correctly, so I assume there is a problem in passing the request header in angular, can anyone tell me where am I wrong, please? I specify that I get this error only in the methods where the OidcUser is present, the rest works perfectly. Thank you!
You need to send an access token with your request. Like this:
import { Component, OnInit } from '#angular/core';
import { OktaAuthService } from '#okta/okta-angular';
import { HttpClient } from '#angular/common/http';
import sampleConfig from '../app.config';
interface Message {
date: string;
text: string;
}
#Component({
selector: 'app-messages',
templateUrl: './messages.component.html',
styleUrls: ['./messages.component.css']
})
export class MessagesComponent implements OnInit {
failed: Boolean;
messages: Array<Message> [];
constructor(public oktaAuth: OktaAuthService, private http: HttpClient) {
this.messages = [];
}
async ngOnInit() {
const accessToken = await this.oktaAuth.getAccessToken();
this.http.get(sampleConfig.resourceServer.messagesUrl, {
headers: {
Authorization: 'Bearer ' + accessToken,
}
}).subscribe((data: any) => {
let index = 1;
const messages = data.messages.map((message) => {
const date = new Date(message.date);
const day = date.toLocaleDateString();
const time = date.toLocaleTimeString();
return {
date: `${day} ${time}`,
text: message.text,
index: index++
};
});
[].push.apply(this.messages, messages);
}, (err) => {
console.error(err);
this.failed = true;
});
}
}
On the Spring side, if you want it to accept a JWT, you'll need to change to use Jwt instead of OidcUser. Example here.
#GetMapping("/")
public String index(#AuthenticationPrincipal Jwt jwt) {
return String.format("Hello, %s!", jwt.getSubject());
}

JHipster spring controller with microservices

I have a JHipster gateway+microservice application. I have added a spring service with jhipster spring-controller and then edited the code like this:
#RestController
#RequestMapping("/api/data")
public class DataResource {
/**
* GET vin
*/
#GetMapping("/vin")
public ResponseEntity<Object> vin(#Valid #RequestBody String address) {
Chart3DataDTO[] data=new Chart3DataDTO[15];
for (int i=0;i<15;i++){
data[i]=new Chart3DataDTO(System.currentTimeMillis()+i, 200+i, 201+i, 202+i);
}
return ResponseEntity.ok(data);
}
For completeness, this is the DTO
public class Chart3DataDTO {
private Long xAxis;
private Integer[] yAxis=new Integer[3];
public Chart3DataDTO(Long xAxis, Integer yAxis1, Integer yAxis2, Integer yAxis3) {
this.xAxis = xAxis;
this.yAxis = new Integer[]{yAxis1, yAxis2, yAxis3};
}
public Long getxAxis() {
return xAxis;
}
public Integer[] getyAxis() {
return yAxis;
}
}
Then I have dockerized gateway and microservice, jhipster docker-compose and started all. Everything works but when the Angular frontent asks for /api/data/vin I get:
if not logged in: 401 (which is fine)
if logged in: the JHipster page 'an error has occurred', instead of returning the JSON of the DTO
What did I miss?
Also, it doesn't appear listed on the Jhipster registry API
2ND EDIT: Added client angular code
import { Injectable } from '#angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json',
'Access-Control-Allow-Origin':'*'
})
};
//const apiUrl = 'api/vin';
const apiUrl = '/api/data/vin';
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http: HttpClient) {}
/*
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
*/
getInputVoltage(address: String): Observable<[]> {
return this.http.get<[]>(`${apiUrl}` + '?address=' + address,httpOptions);
}
}
And
import { Component, OnInit } from '#angular/core';
import * as Highcharts from 'highcharts';
import { ApiService } from '../api.service';
#Component({
selector: 'jhi-device-graph',
templateUrl: './device-graph.component.html',
styleUrls: ['./device-graph.component.scss']
})
export class DeviceGraphComponent implements OnInit {
Highcharts: typeof Highcharts = Highcharts;
chartOptions: Highcharts.Options = {
xAxis: {
type: 'datetime'
},
yAxis: {
title: {
text: 'Voltage'
}
},
title: {
text: 'Input voltage'
},
series: [
{
data: [
[Date.UTC(2010, 0, 1), 29.9],
[Date.UTC(2010, 2, 1), 71.5],
[Date.UTC(2010, 3, 1), 106.4]
],
type: 'line',
name: 'Vin1'
},
{
data: [
[Date.UTC(2010, 0, 1), 39.9],
[Date.UTC(2010, 2, 1), 91.5],
[Date.UTC(2010, 3, 1), 96.4]
],
type: 'line',
name: 'Vin2'
}
]
};
data: String[] = [];
isLoadingResults = true;
constructor(private api: ApiService) {}
ngOnInit(): void {
this.api.getInputVoltage('10.1.30.1').subscribe(
(res: any) => {
this.data = res;
//console.log(this.data);
this.isLoadingResults = false;
},
err => {
//console.log(err);
this.isLoadingResults = false;
}
);
}
}
Your angular client sends a GET request on /api/data/vin with query parameters while your REST controller expects a request body, this can't work.
Your controller must expect a #RequestParam
Also, as the request goes through a gateway, it must be prefixed by /services and your service name, so in your case the URL is /services/graph/api/data/vin.
Also using #Valid on a String does not do anything unless you add some other validation annotations like #NotBlank or #Size(max=30)
#GetMapping("/vin")
public ResponseEntity<Object> vin(#Valid #RequestParam String address) {
#RequestBody must be used only for POST or PUT.
Assuming you are sending a parameter when you make the get call (seeing that #RequestBody address) and without seeing the logs, you can try changing the ResponseEntity<Object> to ResponseEntity<Chart3DataDTO[]>

Reconnect WebSocket in Angular 5 with Rxjs observable and observer

In any case, if my application got disconnected from WebSocket I am not able to reconnect it. I am attaching the sample code please suggest me the idea to how I can reconnect WebSocket and initialize my identity on web socket server again.
I have made my application with the help of this tutorial.
https://tutorialedge.net/typescript/angular/angular-websockets-tutorial/
I have written the same code in my application except for my application requirement.
The tutorial that I have been followed does not have the feature to reconnect the WebSocket in any case like internet break or by some reason our WebSocket server got restart because I am running my WebSocket server with SupervisorD and it will automatically restart if WebSocket server get to stop in any case
My application is in production and many customers are using now so I can not change all flow and recreate the code for WebSocket in this application.
I am adding all code that I am using
websocket.service.ts
import { Injectable } from '#angular/core';
import * as Rx from 'rxjs/Rx';
#Injectable()
export class WebsocketService {
connected: boolean = false;
initialized: boolean= false;
constructor() { }
private subject: Rx.Subject<MessageEvent>;
public connect(url): Rx.Subject<MessageEvent> {
if (!this.subject) {
this.subject = this.create(url);
// console.log("Successfully connected: " + url);
}
return this.subject;
}
private create(url): Rx.Subject<MessageEvent> {
let ws = new WebSocket(url);
// here i am trying to reconnect my websocket
// setInterval (function () {
// if (ws.readyState !== 1) {
// ws = new WebSocket(url);
// this.initialized = false;
// }
// console.log(this.initialized);
// if (ws.readyState == 1 && this.initialized == false) {
// ws.send('{"type":"add",
"t":"14bfa6xxx", "from_numbers":
["xxxx","xxxxx"], "platform":"xxxx"}');
// this.initialized = true;
// }
// console.log(this.initialized);
// }, 4000);
let observable = Rx.Observable.create(
(obs: Rx.Observer<MessageEvent>) => {
ws.onmessage = obs.next.bind(obs);
ws.onerror = obs.error.bind(obs);
ws.onclose = obs.complete.bind(obs);
return ws.close.bind(ws);
})
let observer = {
next: (data: Object) => {
if (ws.readyState === WebSocket.OPEN) {
if (data['type'] == 'add') {
console.log("Connection Initialized");
}
ws.send(JSON.stringify(data));
}
}
}
return Rx.Subject.create(observer, observable);
}
}
Chat.service.ts
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { WebsocketService } from './websocket.service';
#Injectable()
export class ChatService {
public messages: Subject<Message>;
constructor(wsService: WebsocketService, private authService: AuthService) {
this.messages = <Subject<Message>>wsService
.connect(socket_url)
.map((response: MessageEvent): Message => {
const data = JSON.parse(response.data);
console.log(data);
return data;
});
}
}
and finality I have used this in our component to subscribe message.
constructor(private chatService: ChatService,) {
this.socketMessages();
}
socketMessages() {
this.chatService.messages.subscribe(msg => {
console.log(msg)
});
}

Could not find acceptable representation for file download

I want to implement file download using this Angular 6 code:
Rest API:
private static final Logger LOG = LoggerFactory.getLogger(DownloadsController.class);
private static final String EXTERNAL_FILE_PATH = "/Users/test/Documents/blacklist_api.pdf";
#GetMapping("export")
public ResponseEntity<FileInputStream> export() throws IOException {
File pdfFile = Paths.get(EXTERNAL_FILE_PATH).toFile();
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity.ok().headers(headers).contentLength(pdfFile.length())
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new FileInputStream(pdfFile));
}
Service:
import {Injectable} from '#angular/core';
import {HttpClient, HttpParams} from "#angular/common/http";
import {Observable} from "rxjs/index";
import {environment} from "../../../environments/environment";
import {HttpUtils} from "../common/http-utils";
import { map } from 'rxjs/operators';
import {Http, ResponseContentType} from '#angular/http';
#Injectable({
providedIn: 'root'
})
export class DownloadService {
constructor(private http: HttpClient) {
}
downloadPDF(): any {
return this.http.get(environment.api.urls.downloads.getPdf, {
responseType: 'blob'
})
.pipe(
map((res: any) => {
return new Blob([res.blob()], {
type: 'application/pdf'
})
})
);
}
}
Component:
import {Component, OnInit} from '#angular/core';
import {DownloadService} from "../service/download.service";
import {ActivatedRoute, Router} from "#angular/router";
import {flatMap} from "rxjs/internal/operators";
import {of} from "rxjs/index";
import { map } from 'rxjs/operators';
#Component({
selector: 'app-download',
templateUrl: './download.component.html',
styleUrls: ['./download.component.scss']
})
export class DownloadComponent implements OnInit {
constructor(private downloadService: DownloadService,
private router: Router,
private route: ActivatedRoute) {
}
ngOnInit() {
}
export() {
this.downloadService.downloadPDF().subscribe(res => {
const fileURL = URL.createObjectURL(res);
window.open(fileURL, '_blank');
});
}
}
The file is present in the directory but when I try to download it I get error:
18:35:25,032 WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-2) Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
Do you know how I can fix this issue?
Do I need to add additional configuration in order to download the file via Angular web UI?
I use spring-boot-starter-parent version 2.1.0.RELEASE
FileSaver npmjs.com/package/ngx-filesaver is the best library for file download in Angular6 but it has various issues in ios devices. We fixed it by writing own methods and conditionally handling it .
Component
download() {
this.downloadService.downloadPDF().subscribe(async (res: Blob) => {
if (this.isIOSMobileDevice) {
const file = new File([res], fileName, { type: 'application/pdf' });
const dataStringURL: any = await this.fileService.readFile(file);
this.hrefLink = this.sanitizer.bypassSecurityTrustUrl(dataStringURL);
} else {
saveFile(res, fileName);
}
});
}
export const saveFile = (blobContent: Blob, fileName) => {
const isIOS = (!!navigator.platform.match(/iPhone|iPod|iPad/)) || (navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2);
const blob = new Blob([blobContent], { type: 'application/pdf' });
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
document.body.appendChild(link);
link.href = url;
link.target = '_self';
link.download = fileName;
link.click();
document.body.removeChild(link);
}
};
File Service
async readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
HTML code
<a *ngIf="isIOSMobileDevice" [href]="hrefLink"
target="_blank">Download</a>
<a *ngIf="!isIOSMobileDevice" href="javascript:;" (click)="download"
target="_blank">Download</a>
For ios Mobile devices ,download method has to be called in prerequisite so that we get hrefLink.

Resources