Reputation: 162
I am new to Nest.JS and apparently don't understand how to use observables so hopefully ya'll can help.
Basically I have a method that needs to: first: login to hashicorp vault and return a client_token via an http call. second: if we got a token back from vault, we then check that the request contained a certification-id, if not we have to request a new certification to be generated. Which requires the client_token from vault.
The problem I am having is that when I call vault to get the client_token, it does not get returned in time for me to be able to use it to generate a new cert via a second api call.
What can I do in order to be able to use the client_token in the next step?
Here is the code for my latest attempt:
Controller:
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post('getUserCert')
async getUserCert(@Body() loginDto: vaultLoginReqDto) {
return this.userService.getCertificate(loginDto);
}
}
Controller calls the getCertificate method:
getCertificate(loginDto: vaultLoginReqDto) {
this.loginToVault(loginDto);
if (this.vault_token) {
if (loginDto.cert_id) {
this.checkForExistingCert(loginDto);
} else {
this.generateNewCert(this.vault_token);
}
} else {
throw new Error('User is not authorized to access Vault.');
}
}
The logon method:
loginToVault(loginDto: vaultLoginReqDto) {
const url = 'http://vault:8200/v1/auth/jwt/login';
const payload: vaultLoginReqDto = {
jwt: loginDto.jwt,
role: loginDto.role,
};
try {
this.httpService
.post(url, payload)
.subscribe((res: AxiosResponse<vaultLoginResDto>) => {
this.vault_token = res.data.auth.client_token;
});
} catch (e) {
this.throwError(e, url, 'Unable to login to vault');
}
}
the problem method is the generateNewCert method. It is not getting the vault_token in time.
generateNewCert(vault_token: string): Observable<string> {
const url = `http://127.0.0.1:8200/v1/xxxx/xxxx/issue/reader`;
const payload = {
common_name: 'id.xxxx.com',
};
const headers = {
'X-Vault-Token': vault_token,
};
try {
return this.httpService.post(url, payload, { headers: headers }).pipe(
map((res: AxiosResponse<vaultGetCertResDto>) => {
return res.data.data.certificate;
}),
);
} catch (e) {
this.throwError(e, url);
}
}
I appreciate the help!
Upvotes: 2
Views: 13262
Reputation: 661
If you decide to stick with the observables, you can return an observable from the loginToVault method as opposed to subscribing to it
loginToVault(loginDto: vaultLoginReqDto): Observable<string> {
const url = 'http://vault:8200/v1/auth/jwt/login';
const payload = {
jwt: loginDto.jwt,
role: loginDto.role,
};
return this.httpService
.post(url, payload)
.pipe(
catchError(() => { /* handle errors */ }),
map((res) => res.data.auth.client_token)
)
}
Then in getCertificate method, you subscribe to loginToVault and handle the logic
getCertificate(loginDto: vaultLoginReqDto) {
this.loginToVault(loginDto)
.pipe(
tap(vault_token => {
if (!vault_token) {
throw new Error('User is not authorized to access Vault.');
}
})
)
.subscribe(vault_token => loginDto.cert_id ?
this.checkForExistingCert(loginDto) :
this.generateNewCert(vault_token)
)
}
The vault_token is passed from one service to another and thus will be accessible in the generateNewCert method. You do not need to declare it globally
Upvotes: 2
Reputation: 787
The easiest way to make it work is the convert to a Promise so you can wait for the result.
loginToVault(loginDto: vaultLoginReqDto) {
const url = 'http://vault:8200/v1/auth/jwt/login';
const payload = {
jwt: loginDto.jwt,
role: loginDto.role,
};
return this.httpService
.post(url, payload)
.pipe(
catchError(() => {/** ...handleError **/}),
map((res) => {
this.vault_token = res.data.auth.client_token;
return this.vault_token;
}),
)
.toPromise()
}
Now, you can use async
/ await
at getCertificate
async getCertificate(loginDto: vaultLoginReqDto) {
await this.loginToVault(loginDto);
// or const vault_token = await this.loginToVault(loginDto)
if (this.vault_token) {
if (loginDto.cert_id) {
this.checkForExistingCert(loginDto);
} else {
this.generateNewCert(this.vault_token);
}
} else {
throw new Error('User is not authorized to access Vault.');
}
}
Upvotes: 4