How to retrieve Azure Key Vault in React JS - reactjs

I have created some setting in Azure and I need fetch some secret keys from there in react js
const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
var KEY_VAULT_URI = "https://mydomain.com.vault.azure.net/";
msRestAzure.loginWithAppServiceMSI({resource: 'https://vault.azure.net', msiEndpoint: 'https://vault.azure.net', msiSecret: '69418689F1E342DD946CB82994CDA3CB', msiApiVersion: '' }).then((credentials) => {
const keyVaultClient = new KeyVault.KeyVaultClient(credentials);
var data = keyVaultClient.getSecret(KEY_VAULT_URI, 'My_Secret_Key');
console.log(data);
});
I'm getting some issue net::ERR_NAME_NOT_RESOLVED, I think I'm missing something. Could anyone please suggest that how to retrieve that secret keys from Azure in React Js

Using the loginWithAppServiceMSI() method from ms-rest-azure will autodetect if you're on a WebApp and get the token from the MSI endpoint. So you must host your code on Azure webapp. Refer to this article for more details.
function getKeyVaultCredentials(){
return msRestAzure.loginWithAppServiceMSI({resource: 'https://vault.azure.net'});
}
function getKeyVaultSecret(credentials) {
let keyVaultClient = new KeyVault.KeyVaultClient(credentials);
return keyVaultClient.getSecret(KEY_VAULT_URI, 'secret', "");
}
getKeyVaultCredentials().then(
getKeyVaultSecret
).then(function (secret){
console.log(`Your secret value is: ${secret.value}.`);
}).catch(function (err) {
throw (err);
});
If you don't have to use Managed Service Identity (MSI), you can use msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain) to get the credentials.
function getKeyVaultCredentials(){
return msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain);
}

Related

Using a blazor server with signalR as a relay server

The goal is to use a Blazor server as a relay server using signalR.
I have little to no experience with blazor servers before this.
The Idea would be to connect a Winform/Xamarin client to this server, target the recipient using a name/id from an existing database, and relay the necessary info.
Hub:
[Authorize]
public class ChatHub : Hub
{
public Task SendMessageAsync(string user, string message)
{
//Context.UserIdentifier
Debug.WriteLine(Context.UserIdentifier);
Debug.WriteLine(Context?.User?.Claims.FirstOrDefault());
return Clients.All.SendAsync("ReceiveMessage", user, message); ;
}
public Task DirectMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}
}
As per documentation I'm trying to set the Context.UserIdentifier, I do however struggle with the authentication part. My program.cs looks like this:
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
services.AddTransient<IUserIdProvider, MyUserIdProvider>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
//var accessToken = context.Request.Query["access_token"];
var accessToken = context.Request.Headers["Authorization"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/chathub"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSignalR();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.MapBlazorHub();
app.MapHub<ChatHub>("/chathub");
app.MapFallbackToPage("/_Host");
app.Run();
As for my Client (a winform test client) I tried something like this:
HubConnection chatHubConnection;
chatHubConnection = new HubConnectionBuilder()
.WithUrl("https://localhost:7109/chathub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.WithAutomaticReconnect()
.Build();
private async void HubConBtn_Click(object sender, EventArgs e)
{
chatHubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
this.Invoke(() =>
{
var newMessage = $"{user}: {message}";
MessagesLB.Items.Add(newMessage);
});
});
try
{
await chatHubConnection.StartAsync();
MessagesLB.Items.Add("Connected!");
HubConBtn.Enabled = false;
SendMessageBtn.Enabled = true;
}
catch (Exception ex)
{
MessagesLB.Items.Add(ex.Message);
}
}
As a first step I'm just trying to authenticate a user/check that it's in the live database, if so connect and fill out: Context.UserIdentifier so I can use this within the Hub. I understand that I probably need a middleware however I don't really know exactly how to test a connectionId/Jwt token or similar to get the user/connection.
Any nudge in the right direction would be appreciated.
If I understand your question you don't know where and how to generate a JWT token.
For me the JWT token should be generated from the server, your hub.
POST api/auth and in the playload you give login + SHA256 password and returns JWT token.
Once you checked the user auth is correct in you DB you can issue the token.
To generate a JWT token I use this piece of code.
public string GenerateToken(IConfiguration Config, DateTime? expire)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, _id),
new Claim(ClaimsIdentity.DefaultRoleClaimType, role)
};
// ClaimsIdentity.DefaultRoleClaimType
var bytes = Encoding.UTF8.GetBytes(Config["jwt:Secret"]);
var key = new SymmetricSecurityKey(bytes);
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
//Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
var token = new JwtSecurityToken(
//Config.GetValue<string>("jwt:Issuer"),
//Config.GetValue<string>("jwt:Issuer") + "/ressources",
claims: claims,
expires: DateTime.Now.AddMinutes(Config.GetValue<int>("jwt:ExpireMinute")),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
#edit
Look here to allow JWT for SignalR
https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-6.0
I also added this.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
The easiest solution would be to use something like IdentityServer to handle the authentication. It's a free solution, also .NET based which takes very little configuration effort to offer you simple client credentials authentication and generate the token for you.
I did basically exactly what you're asking here: A WinForms application connecting to my signalR hub application on a remote server, using Bearer token - but I also have OIDC/OAUTH implemented with third party user account login.
IdentityServer offers a great repository of full examples that showing you all the flow - and with just a few lines of code changed, you have a fullblown authentication system, which can be enhanced easily.
With IdentityServer you get everything, even the corresponding extension methods that enable your signalR hub application to create the claims principal (aka user) from the claims included within your token.
Here you'll find all the examples and docs:
https://github.com/IdentityServer/IdentityServer4
If you hit any walls, just reply here and I'll try to help.

AWS JS SDK: How do I access GameLift data from a subaccount / another role using the root IAM account?

I'm not too familiar with the terms, so please bear with me.
I'm working on a CMS site using react. We've already got logon via AWS Cognito in place, and we used to have a page that displays GameFleet data.
However, the Aliases and Fleets have been moved to a subaccount:
And as such the GameFleet page is empty.
I've initially overcome this problem by creating an IdentityPool (and roles) for the DevRole subaccount, as the CMS retrieves the GameFleet data via the following code:
componentDidMount() {
AWS.config.region = REGION;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: IDENTITY_POOL_ID // <--- Changed this to new IdentityPoolId
});
AWS.config.credentials.get(function(err) {
if (err) console.log(err);
else console.log(AWS.config.credentials);
});
await this.requestGameLiftData();
};
requestGameLiftData = async () => {
const gamelift = new AWS.GameLift();
try {
const aliasData = await new Promise((resolve, reject) => {
gamelift.listAliases({}, function(err, data) {
if (err) { reject("Aliases failed");}
else { resolve(data); }
});
});
} catch (error) {
console.log(error);
}
};
But the problem now is that there is a new subaccount, one I don't have access to, and I can foresee that new subaccounts might be created which won't have the necessary IdentityPoolId required for my approach.
I've been told accessing the subaccount GameLift data from the root account should be possible, but I'm not sure how. I've been looking at the IAM page under the main account, but there doesn't seem to be anything there that could point to the subaccounts.
Am I missing anything?
You need to use AWS Assume roles functionality here using which your primary account can assume role of secondary account and get temporary credentials of sub-account which can be used to pull the data from sub-account from primary account.
https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html

How to retrieve secret key from Azure Key Vault in React JS

How can I fetch the secret key from azure key vault in reactjs application? I am passing my secret key in headers like below
headers: {
"subscription-key": "xxxxxxxxxxxxx",
"content-type": "application/json"
}
How can I get the "subscription-key" value from azure key vault? I added my subscription-key value in the azure key vault.
In local, you can sue loginWithServicePrincipalSecret with clientid and secret to get the credential.
function getKeyVaultCredentials(){
return msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain);
}
function getKeyVaultSecret(credentials) {
let keyVaultClient = new KeyVault.KeyVaultClient(credentials);
return keyVaultClient.getSecret(KEY_VAULT_URI, 'secret', "");
}
getKeyVaultCredentials().then(
getKeyVaultSecret
).then(function (secret){
console.log(`Your secret value is: ${secret.value}.`);
}).catch(function (err) {
throw (err);
});
Also, you could use the loginWithAppServiceMSI() method from ms-rest-azure will autodetect if you're on a WebApp and get the token from the MSI endpoint. So you must host your code on Azure webapp. Refer to this article for more details.
function getKeyVaultCredentials(){
return msRestAzure.loginWithAppServiceMSI({resource: 'https://vault.azure.net'});
}

Error when using Google Application Default Credentials on App Engine

I am trying to make a Node.js app (running Express on App Engine) authenticate with Google API (Server-to-Server) using the Google Application Default Credentials. The app is supposed to use the credentials to talk with Google Analytics, which I have set up by turning on the Analytics API in the Google Developers Console. This is the code I have implemented:
var google = require('googleapis')
var analytics = google.analytics('v3')
app.post('/getAnalyticsData', (req, res) => {
google.auth.getApplicationDefault(function(err, authClient) {
if (err) {
/* Handle error */
}
if (authClient) {
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
authClient = authClient.createScoped(['https://www.googleapis.com/auth/analytics.readonly'])
}
analytics.data.ga.get({
'auth': authClient,
'ids': 'ga:VIEW_ID',
'metrics': 'ga:pageviews,ga:sessions',
'start-date': '2017-01-01',
'end-date': '2017-03-09'
}, function(err, response) {
if (err) {
console.log("Analytics error: ", err)
}
if (response) {
console.log("YAY! Analytics response: ", response)
/* Do something with the response */
}
})
}
})
})
But I am getting this error: A Forbidden error was returned while attempting to retrieve an access token for the Compute Engine built-in service account. This may be because the Compute Engine instance does not have the correct permission scopes specified. Insufficient Permission.
Any idea how to solve this and succeed with the authentication?
I had the same error when trying to use google-auth-library to connect to datastore and was unable to set the correct permissions for the default service account. I found an example in their samples folder that created an auth client using a key file. You can create your own service account with the right permissions and generate a key file on the service account admin page in the cloud console. Hope this helps.
const {auth} = require('google-auth-library');
async function getDnsInfo() {
const client = await auth.getClient({
keyFile: 'path/to/keyFile.json,
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;
const res = await client.request({url});
console.log('DNS Info:');
console.log(res.data);
}

Error: User credentials required in Google Cloud Print API

I'm trying to use Google Cloud Print(GCP) API, but I can't make it works.
Maybe I've understood bad the workflow because is the first time I'm using the google api, please help me to understand how to make it works.
Initial considerations:
I'm trying to implement it in reactJS, but It is indifferent because the logic to make GCP works is independent of the technology. Then you also can help me understand the workflow.
What exactly I want:
To make my first test, I am looking to get all information about my printer.
What I did:
I created a project in: https://console.developers.google.com
Inside the project created, I created a credential:
create credentials -> OAuth client ID
And I chose Application type: Web, and also configure the restrictions to source and redirection to my localhost.
Manually in https://www.google.com/cloudprint, I added my printer, I made a test printing a PDF and was OK.
I created a project in reactJS to get the information of my printer I've added.
Component:
Explanation:
I'm using a component react-google-login to obtain easily the user accessToken: https://github.com/anthonyjgrove/react-google-login
This component only obtains the access token and save it in localStorage, in a variable called googleToken and it draws a button to call a function to obtain the information about the printer.
code:
import React, { Component } from 'react'
import GoogleLogin from 'react-google-login';
import { connect } from 'react-redux'
import { getPrinters } from '../actions/settings'
class Setting extends Component {
responseGoogle(response) {
const accessToken = response.accessToken
localStorage.setItem('googleToken', accessToken)
}
render() {
return (
<div>
<GoogleLogin
clientId="CLIENT_ID_REMOVED_INTENTIONALLY.apps.googleusercontent.com"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
/>
<button
onClick = {() => {
this.props.getPrinters()
}}
>test printer</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state
}
}
const mapDispatchToProps = dispatch => {
return {
getPrinters() {
dispatch(getPrinters())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Setting)
Action or Function to get information printer:
Explanation:
I'm passing the parameter printerid to get information about that printer.
In authorization, I'm using OAuth ... because in the documentation says that(second paragraph).: https://developers.google.com/cloud-print/docs/appInterfaces
The next two headers I wrote it because I tried solutions as:
Google Cloud Print API: User credentials required
Google Cloud Print User credentials required
code:
import axios from 'axios'
axios.defaults.headers.common['Authorization'] = 'OAuth ' + localStorage.getItem('googleToken')
axios.defaults.headers.common['scope'] = 'https://www.googleapis.com/auth/cloudprint'
axios.defaults.headers.common['X-CloudPrint-Proxy'] = 'printingTest'
const getPrinters = () => {
return () => {
return axios.get('https://www.google.com/cloudprint/printer'
, {
params: {
printeid: 'PRINTER_ID_REMOVED_INTENTIONALLY'
}
}
)
.then(response => {
console.log('response of google cloud print')
console.log(response)
})
}
}
export { getPrinters }
Error:
After all explained before, I got the next error:
User credentials required
Error 403
Note:
I'm using CORS plugin by recommendation of:
Chrome extensions for silent print?
because initially, I had cors error.
Any suggestion or recommendation would be very useful, thanks.
I've resolved my problem, my main problem about User Credential required were because I was using the incorrect access token and It was because I was getting the access token incorrectly.
I'm going to explain my whole solution because there are few examples of codes with this API.
Solutions:
The steps described were Ok until the fourth step where I used the external component react-google-login to trying to get the access token, instead I used googleapis module: Link Github googleapis
Also to avoid CORS problem(and not use CORS chrome plugin) I wrote the requests to Google API in server side.(NODEJS)
I had also a problem in the frontend when I tried to generate a popup to give permission for printer(problems about CORS), my solution was to use this very simple module for authentication: Link Github oauth-open
General scheme:
Explanation:
Knowing I have all data described in my question post(until the third step).
Authentication:
The next step in getting a URL and use it to the user can authenticate.
As I said before I used the module oauth-open in the frontend to generate the popup and only this module need the URL. To get the URL in the backend I used the endpoint /googleurl, where here I used the method generateAuthUrl of the module googleapis to generate the URL.
After that In the frontend, I got the authentication_code(that returned the module oauth-open), I send It to my endpoint /googletoken and here I process the authentication_code to generate access token, refresh token and expiration date with the method getToken of the module googleapis. Finally, these data are stored in the database.
Print:
For print, since the frontend, I send what data I need send to the printer. I used my endpoint /print
In the backend endpoint, my logic was the next:
Recover tokens and expiration date from database, with the expiration date check if the token has expired, and if It has already expired then gets another token and replace the old access token with the new one, replacing also with the new expiration date, to obtain this new data only is necessary call to method refreshAccessToken of module googleapis.Note: the refresh token never expires.
After having the access token updated, use it to send data to the printer with Google route(.../submit)
Code:
All the next codes are in only 1 file
Some data as validation, static variables, error handler, etc, has been removed to better understanding.
Route get URL authentication.
const express = require('express');
const google = require('googleapis');
const router = express.Router();
var OAuth2 = google.auth.OAuth2;
const redirect_url = 'http://localhost:3001/setting'; //Your redirect URL
var oauth2Client = new OAuth2(
'CLIENT ID', //Replace it with your client id
'CLIEND SECRET', //Replace it with your client secret
redirect_url
);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/cloudprint'
});
router.get('/googleurl', (req, res) => {
return res.status(200).send({
result: { googleURLToken: url }
});
});
To get tokens using the authentication code and save these in the database.
const Setting = require('../models/setting'); // My model(Mongoose)
router.post('/googletoken', (req, res) => {
oauth2Client.getToken(req.body.code, function (err, tokens) {
oauth2Client.credentials = tokens;
// If refresh token exits save it
// because the refresh token it returned only 1 time! IMPORTANT
if (tokens.hasOwnProperty('refresh_token')) {
let setting = new Setting();
setting.refreshTokenGoogle = tokens.refresh_token;
setting.expirationTokenGoogle = tokens.expiry_date;
setting.tokenGoogle = tokens.access_token;
setting.save()
.then((settingCreated) => {
return res.status(200).send({
message: 'OK'
});
})
}
});
});
To print
const axios = require('axios');
const moment = require('moment');
router.post('/print',async (req, res) => {
const tickeProperties = {
'version': '1.0',
'print': {
'vendor_ticket_item': [],
'color': { 'type': 'STANDARD_MONOCHROME' },
'copies': { 'copies': 1 }
}
};
const accessToken = await getTokenGoogleUpdated();
axios.get(
'https://www.google.com/cloudprint/submit',
{
params: {
printerid : printerID, // Replace by your printer ID
title: 'title printer',
ticket: tickeProperties,
content : 'print this text of example!!!',
contentType: 'text/plain'
},
headers: {
'Authorization': 'Bearer ' + accessToken
}
}
)
.then(response => {
return res.status(200).send({
result: response.data
});
})
}
);
async function getTokenGoogleUpdated() {
return await Setting.find({})
.then(async setting => {
const refreshTokenGoogle = setting[0].refreshTokenGoogle;
const expirationTokenGoogle = setting[0].expirationTokenGoogle;
const tokenGoogle = setting[0].tokenGoogle;
const dateToday = new Date();
// 1 minute forward to avoid exact time
const dateTodayPlus1Minute = moment(dateToday).add(1, 'm').toDate();
const dateExpiration = new Date(expirationTokenGoogle);
// Case date expiration, get new token
if (dateExpiration < dateTodayPlus1Minute) {
console.log('Updating access token');
oauth2Client.credentials['refresh_token'] = refreshTokenGoogle;
return await oauth2Client.refreshAccessToken( async function(err, tokens) {
// Save new token and new expiration
setting[0].expirationTokenGoogle = tokens.expiry_date;
setting[0].tokenGoogle = tokens.access_token;
await setting[0].save();
return tokens.access_token;
});
} else {
console.log('Using old access token');
return tokenGoogle;
}
})
.catch(err => {
console.log(err);
});
}
I hope It helps you if you want to use Google Cloud Print to not waste a lot of time as I did.
The important part there is a scope https://www.googleapis.com/auth/cloudprint which is not obvious and took one day for me to figure out.

Resources