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

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

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.

How do I sort a list of users by name on a node server?

I have created several user accounts on mongodb and i want to sort them out by user name. I compare the user names in the database against a string provided through aaxios request with a body value that is taken from an input value, like this:
frontend
const findUsers = async () => {
try {
const response = await axios.post(`http://localhost:8080/search-users/${_id}`, { searchValue });
setReturnedUser(response.data.matchedUser);
} catch (error) {
console.log(error);
}
}
findUsers();
backend
exports.sort = (req, res) => {
let result;
User.find({ name: req.body.searchValue }).exec((error, users) => {
if (error) {
return res.status(400).json({
message: error,
});
}
result = users;
res.status(200).json({
message: 'Description added successfully',
matchedUser: result,
});
});
};
The problem with this approach is that the users are returned only after I type in the entire name.
What I want is that the users to get returned as I type in the name, so several matching users will het returned when I start typing and as I continue the list will narrow down, until only the matching user remains.
I have successfully achieved this on the react side, but that was possible only by fetching all the users from the database, which would be a very bad idea with a lot of users. The obvious solution is to do the sorting on the server.
Filtering on the client-side is possible but with some tweaks to your architecture:
Create an end-point in node that returns all the users as JSON. Add caching onto this end-point. A call to origin would only occur very infrequently. YOu can then filter the list easily.
Use something like GraphQL and Appollo within node. This will help performance
To do the filtering in node you can use a normal array.filter()
I woul do the filter in mongo as the quick approach and then change it if you notice performance issues. It is better no to do pre-optimisation. As Mongo is NoSQL it wshould be quick

How to retrieve Azure Key Vault in React JS

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);
}

How to use a Cognito group to Perfom Administrator level task in Reactjs

I have succeeded in programatically adding a cognito user to a group in React. Now my problem is Any user from any group can do this but I only want users from the admin group to be capable od doing this. Yes I know I could simply place the method into classes only the admin group can access by routing basses on accessTokens. But I was wondering if I were to for example, give the admin group the IAM-Role AdministratorAccess which technically should give that group access to everything how would I have to change my configuration in React so that this would work. Below is my code but this is the version that uses the accesskeyid and soon of the IAM user but as I said I would like to use the cognito group and not the IAM user. Hope my question is understandable. Thanks for the help.
AWS.config.update({ credentials: {
accessKeyId: "XXXXXXXXXXXXXXXXXXXX",
secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXX",
},
region: 'xx-xx-xx'
});
var params = {
GroupName: 'TestGroup',
UserPoolId: 'XXXXXXXXXXXXXXXXXXXX',
Username: 'testuser',
};
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
cognitoidentityserviceprovider.adminAddUserToGroup(params, function(err, data) {
if (err) console.log('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: ', err, err.stack); // an error occurred
else console.log(data); // successful response
});

AWS. Storing and displaying the profile picture of users

I have a requirement of storing andd displaying the profile picture of my users. So I used the S3.upload function in my nodeJs backend to store the image.
And after the image is stored I stored the link in database to fetch it using ng-source in my view. It worked but the link got expired after few hours and did not work. Below is the code for my upload. Is there any solution in how to do this or any other better way to do this.
var body = fs.createReadStream(file.file.path);
//Upload the photo to AWS
s3.upload({Body: body}, function (err, data) {
if (err) {
res.sendStatus(500);
}
if (data) {
//getSignedUrl and Store it in Database
var AWS = require('aws-sdk');
var url = req.params.doctorId + "/Images/" + fileName;
var s3 = new AWS.S3()
,params = {Bucket: S3Bucket, Key:url };
s3.getSignedUrl('getObject', params, function (err, url) {
if (err || url == null) res.status(500).send({msg: "amazon s3 error"});
else if (url) {
if(req.body.picture == 1) {
User.findByIdAndUpdate(req.params.doctorId, {$set: {'FileName.profilePicture': url}},
function (err, doc) {
if (err)
res.sendStatus(500);
else
res.send({url: url});
});
This is because you're getting the URL from a signed URL and signed URLs expire by design.
From Share an Object with Others on AWS docs:
All objects by default are private. Only the object owner has permission to access these objects. However, the object owner can optionally share objects with others by creating a pre-signed URL, using their own security credentials, to grant time-limited permission to download the objects.
It seems like you're not exactly storing "secret" resources here that access has to be granted to, then the best approach here is store the image publicly. This is trivial to do and you simply have to set the ACL to public-read when you call PutObject or upload. That way you'll know the URL for the object without actually having to retrieve it:
https://s3-[region].amazonaws.com/[bucket]/[file]
This is what your upload statement would look like then:
s3.upload({ Body: body, ACL: 'public-read' }, function (err, data) {

Resources