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'});
}
Related
I created a ASP.NET Core Web API and React in one web application and deployed to production.
The end-points are:
www.myserver.com/obs is the front-end app.
www.myserver.com/obs/api/GetValue is the web API.
How do you secure the Web API endpoints so that only requests from the react application is able to call the API?
For example, if I were to do a Postman call on a remote machine to www.myserver.com/obs/api/GetValue it should not return the resource.
One way is to use an API Key however where would you put the API-Key on the react side? I read that you can put it in .env file however in production you can still find the file using dev-tools.
Another option I read is to create a proxy API that the react app calls and the proxy has the API Key but that seems to be overkill, is there a simpler way that I have missed?
You can't. Your react app is readable by the browser, and therefore readable by anyone who knows how to use browser developer tools or intercept HTTP(s) requests on their computer.
If your react app can talk to your API, so can anyone else. The same goes for a proxy. You can find a more detailed answer here.
If you want to control access you could introduce authentication, and only grant access to trusted users, but you still can't stop them from accessing your API outside of your react app if they really wanted to.
There are steps you can take to make it more difficult. I would recommend that you read up on creating secure APIs. Some links to get you started:
https://security.stackexchange.com/questions/58104/secure-a-publicly-accessible-rest-api
https://developer.okta.com/blog/2019/09/04/securing-rest-apis
https://restfulapi.net/security-essentials/
One way is to use an API Key however where would you put the API-Key
on the react side?
Yes, you could create an API Key Middleware and use it to authenticate the request. If the request is from the react application, you could add the API key in the request header. Code like this:
Using fetch method:
fetch('/api/MoviesAPI', {
method: 'Get', // or 'Post'
headers: {
'Content-Type': 'application/json',
'ApiKey':'Test-value',
},
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.log('Error:', error);
});
Using Ajax method:
$.ajax({
type: "Get",
url: "/api/MoviesAPI", //remember change the controller to your owns.
contentType: 'application/json',
beforeSend: function (xhr) { xhr.setRequestHeader('ApiKey', 'test-value'); },
success: function (data) {
console.log(data)
},
failure: function (response) {
console.log(response.responseText);
},
error: function (response) {
console.log(response.responseText);
}
});
More detail information about sending request with custom header in reactjs, you can search "reactjs call api with custom headers" using Google or Bing, there have lots of articles related it.
Besides, about creating an API key Middleware, you can refer the following steps:
create an ApiKeyMiddleware.cs class in the API application, and add the following code:
public class ApiKeyMiddleware
{
public ApiKeyMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_configuration = configuration;
}
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.StartsWithSegments(new PathString("/api")))
{
//Let's check if this is an API Call
if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
{
// validate the supplied API key
// Validate it
var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
await ValidateApiKey(context, _next, headerKey);
}
else
{
await _next.Invoke(context);
}
}
else
{
await _next.Invoke(context);
}
}
private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
{
// validate it here
var valid = false;
var Apikey = _configuration["ApiKey"];
if (key != null && key==Apikey)
{
valid = true;
}
if (!valid)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Invalid API Key");
}
else
{
var identity = new GenericIdentity("API");
var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" });
context.User = principal;
await next.Invoke(context);
}
}
}
Register this Middleware in the Configure method in the Startup.cs file.
app.UseMiddleware<ApiKeyMiddleware>(); //add APIkeyMiddleware
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication(); //Call the UseAuthentication
app.UseAuthorization();
In the API controller or action method, add Authorize attribute.
[HttpGet]
[Authorize]
public async Task<ActionResult> GetMovie()
{
return Ok(await _context.Movie.ToListAsync());
}
Then, if the request header doesn't contain the ApiKey or the key value is invalid, it will not return the resource.
Edit:
About the API key value, you could store them in the appsettings.json file or In memory .NET objects. When using it you could get it from the Configuration.
For example: store it in the appsettings.json file:
{
...
"Apikey": "my Test API key"
}
Then, using the following code to get the key value
public ApiKeyMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_configuration = configuration;
}
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key)
{
// validate it here
var valid = false;
//get the key value from configuration.
var Apikey = _configuration["ApiKey"];
...
On the react side, you could create a service to get this key value, then send a request with the api key.
I am using a Google Cloud Storage bucket to upload some of my users files. I do not want them to appear as public, so I created a service account representing my frontend app.
I want to know how to make Authenticated Request to Google Cloud Storage, without using the #google-cloud/storage npm package from my frontend app.
I know I need to include Auhtorization: Bearer <token> in my request headers but, how do I get this token ?
I'm using React on my frontend app.
Google has a number of libraries that you can use. Here is one example:
var { google } = require('googleapis')
const request = require('request')
// The service account JSON key file to use to create the Access Token
let privatekey = require('/path/service-account.json')
let scopes = 'https://www.googleapis.com/auth/devstorage.read_only'
let jwtClient = new google.auth.JWT(
privatekey.client_email,
null,
privatekey.private_key,
scopes
)
jwtClient.authorize(function(err, _token) {
if (err) {
console.log(err)
return err
} else {
console.log('token obj:', _token)
console.log('access token:', _token.access_token)
headers: {
"Authorization": "Bearer " + _token.access_token
}
// Make requests to Cloud Storage here
}
})
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);
}
I've been stuck on this issue for a while now, I'm using ADAL.js on the front-end to handle login and authentication. Once logged in I need to get the info for the user (roles, groups, name etc...) however I can't get anything back from the /adfs/userinfo endpoint other than a 401.
So far I log the user in and get back an id_token and access token (or "adal.access.token.key{guid}" in the browser) which is identical to the access key. Due to a cors issue on the front-end I then send this to a back-end mvc core 2.2 controller to make the call to /adfs/userinfo which is where I get the 401. Javascript code below
this.adalAuthentication.Instance.acquireToken(this.adalAuthentication.Instance.config.clientId, (error, token) => {
if (error || !token) {
console.log('ADAL Error Occurred: ' + error);
return;
}
axios({
method: 'get',
url: '/identity/completeLogin/' + token,
headers: {
'Authorization': 'Bearer ' + token
}
}).then((response) => { console.log(response.data) });
});
And controller action...
[HttpGet("completeLogin/{access_token}")]
public async Task<HttpResponseMessage> CompleteLogin(string access_token)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth" + access_token);
var response = await client.GetAsync("https://adfs.server/adfs/userinfo");
response.EnsureSuccessStatusCode();
try
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
catch (Exception e)
{
throw(e);
}
}
At this point I'm stumped, I'm thinking I either can't use ADAL for this or perhaps need to use oidcinstead of OAuth/jwt but I don't want to have to rewrite lots just to find out that doesn't work either or there's a better/best practice way of doing it. Has anyone had this issue before and/or can point me in the right direction or can see where I'm going wrong?
Other things I've tried;
hitting adfs/oauth/token endpoint (just returns with adfs server error)
setting Authorisation on the backend to client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer" + access_token); (just returns invalid token).
Making the front and backend a post method and using the getCachedToken method on the ADAL AuthenticationContext
EDIT: I also have this question open with a the slightly more specific goal of getting an access token with the id token
There's a Postman sample here.
Be aware that "userinfo" only returns a "sub" claim.
I don't know if I'm just not looking in the right places, but I cannot seem to find the right guidance on where to begin working with React / .NET Core 2.1 Web API and (on-prem) Active Directory authentication.
I'm relatively new to .NET authentication in general, and completely new to Active Directory authentication.
I started by using the .NET Core 2.1 React template and attempting to add auth to it, but got completely lost.
Where do I even start?
For me, step one was to set up JWT authentication, such as described in this MSDN blog post.
Next, I had to find a library to use to check a user against Active Directory. I chose System.DirectoryServices.AccountManagement (available for .NET Core).
Now, I had to create a new controller with an [AllowAnonymous]attribute. I called it LoginController, and created an action that looked like the following:
[AllowAnonymous]
[HttpPost]
// Notice: We get a custom request object from the body
public async Task<IActionResult> Login([FromBody] AuthRequest request)
{
// Create a context that will allow you to connect to your Domain Controller
using (var adContext = new PrincipalContext(ContextType.Domain, "mydomain.com"))
{
var result = adContext.ValidateCredentials(request.username, request.password);
if (result)
{
// Create a list of claims that we will add to the token.
// This is how you can control authorization.
var claims = new[]
{
// Get the user's Name (this can be whatever claims you wish)
new Claim(ClaimTypes.Name, request.username)
};
// Read our custom key string into a a usable key object
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration.GetSection("SOME_TOKEN").Value));
// create some signing credentials using out key
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
// create a JWT
var token = new JwtSecurityToken(
issuer: "mydomain.com",
audience: "mydomain.com",
claims: claims, // the claims listed above
expires: DateTime.Now.AddMinutes(30), // how long you wish the token to be active for
signingCredentials: creds);
Since we return an IActionResult, wrap the token inside of a status code 200 (OK)
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token)
});
}
}
}
}
// if we haven't returned by now, something went wrong and the user is not authorized
return Unauthorized();
}
The AuthRequest object could look something like this:
public class AuthRequest
{
public string username { get; set; }
public string password { get; set; }
}
Now, in my React app, all I have to do is make a simple fetch request to the LoginController with the user's username & password that I can get from a login form. The result will be a JWT I can save to state (But should save to cookies: the react-cookie library makes that trivial).
fetch(`login`, {
method: "POST",
headers: {
'content-type': 'application/json',
'accept': 'application/json',
},
body: JSON.stringify({this.state.username, this.state.password})
}).then((response) => {
if (response.status === 401) {
// handle the 401 gracefully if this user is not authorized
}
else {
// we got a 200 and a valid token
response.json().then(({ token }) => {
// handle saving the token to state/a cookie
})
}
})
You now have the ability to add the [Authorize] attribute to any of your controllers in your .NET Core application, and make a fetch request to it while passing your JWT from your React client, like this:
await fetch(`someController/someAction`,
{
method: 'GET'
headers: {
'content-type': 'application/json',
'authorization': `Bearer ${YOUR_JWT}`
}
})
.then(response => doSomething());
If you wanted to use this JWT with a SignalR Hub, add the [Authorize] attribute to your Hub in your .NET Core project. Then, In your React client, when you instantiate the connection to your hub:
import * as signalR from '#aspnet/signalr';
var connection = new signalR.HubConnectionBuilder().withUrl('myHub', { accessTokenFactory: () => YOUR_JWT })
And, viola! A .NET Core React application capable of authorized real-time communication!