I am writing a client to get the continuous messages from RabbitMQ and pushing to AWS SQS service. But I'm not sure about the session expiry, If the session expires do we need to recreate the session or AWS SDK handles it automatically?
log.Printf("PPU Message Broker: Pushing messages to SQS")
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1"),
//Credentials: credentials.NewSharedCredentials("", "sqs_user"),
})
_, err = sess.Config.Credentials.Get()
if err != nil {
log.Fatalf("PPU Message Broker: Credentails Failed")
}
svc := sqs.New(sess)
result, err := svc.SendMessage(&sqs.SendMessageInput{
MessageBody: aws.String(string(data)),
MessageGroupId: aws.String("TestGroup"),
QueueUrl: &qURL,
})
There is a default configuration for session expiration, but you can specify yours:
In addition to NewSession, you can create sessions using
NewSessionWithOptions. This function allows you to control and
override how the session will be created through code, instead of
being driven by environment variables only.
Use NewSessionWithOptions when you want to provide the config profile
Inside of the Options object, there is an attribute for changing the default expiration time, by default is 15 minutes:
// When the SDK's shared config is configured to assume a role this
option
// may be provided to set the expiry duration of the STS credentials.
// Defaults to 15 minutes if not set as documented in the
// stscreds.AssumeRoleProvider.
AssumeRoleDuration time.Duration
https://docs.aws.amazon.com/sdk-for-go/api/aws/session/
Updated answer:
Just noticed the question is about AWS SDK for Go.
From the AWS SDK for Go documentation: https://docs.aws.amazon.com/sdk-for-go/api/aws/credentials/#Credentials.Get
func (*Credentials) Get
func (c *Credentials) Get() (Value, error)
Get returns the credentials value, or error if the credentials Value failed to be retrieved.
Will return the cached credentials Value if it has not expired. If the credentials Value has expired the Provider's Retrieve() will be called to refresh the credentials.
If Credentials.Expire() was called the credentials Value will be force expired, and the next call to Get() will cause them to be refreshed.
Original answer:
From AWS Javascript SDK documentation at https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Credentials.html#get-property
get(callback) ⇒ void
Gets the existing credentials, refreshing them if they are not yet loaded or have expired. Users should call this method before using refresh(), as this will not attempt to reload credentials when they are already loaded into the object.
Related
AWS by default provides retries support on its service calls, which is usually set to a max of 3 attempts.
Can we configure the retry object to set retry attempts to 5?
you can define a custom retry strategy for the SDK to use:
func main() {
sess := session.Must(
session.NewSession(&aws.Config{
// Use a custom retryer to provide custom retry rules.
Retryer: CustomRetryer{
DefaultRetryer: client.DefaultRetryer{
NumMaxRetries: client.DefaultRetryerMaxNumRetries,
}},
// Use the SDK's SharedCredentialsProvider directly instead of the
// SDK's default credential chain. This ensures that the
// application can call Config.Credentials.Expire. This is counter
// to the SDK's default credentials chain, which will never reread
// the shared credentials file.
Credentials: credentials.NewCredentials(&credentials.SharedCredentialsProvider{
Filename: defaults.SharedCredentialsFilename(),
Profile: "default",
}),
Region: aws.String(endpoints.UsWest2RegionID),
}),
)
// Add a request handler to the AfterRetry handler stack that is used by the
// SDK to be executed after the SDK has determined if it will retry.
// This handler forces the SDK's Credentials to be expired, and next call to
// Credentials.Get will attempt to refresh the credentials.
sess.Handlers.AfterRetry.PushBack(func(req *request.Request) {
if aerr, ok := req.Error.(awserr.RequestFailure); ok && aerr != nil {
if aerr.Code() == "InvalidClaimException" {
// Force the credentials to expire based on error code. Next
// call to Credentials.Get will attempt to refresh credentials.
req.Config.Credentials.Expire()
}
}
})
See the sample code here
Yes, AWS provides support to configure their retry and timeouts features. Here are two ways to increase the max number of retries to 5 in AWS Golang SDK v2:
Configure the retry logic on the AWS Config object cfg and it can be used with various AWS service clients using NewFromConfig function
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRetryer(func() aws.Retryer {
return retry.AddWithMaxAttempts(retry.NewStandard(), 5)
}))
client := s3.NewFromConfig(cfg)
Configure the retry logic only for a specific AWS service client
customRetry := retry.NewStandard(func(o *retry.StandardOptions) {
o.MaxAttempts = 5
})
sqsClient := sqs.NewFromConfig(creds,
func(o *sqs.Options) {
o.Retryer = customRetry
},
)
More info can be found at https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/retries-timeouts/ and https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/retry#hdr-Standard
I am creating a session with AWS this way
cfg := aws.NewConfig().WithCredentials(credentials.NewStaticCredentials(accessKeyID, secretAccessKey, "")).WithRegion(region)
sess := session.Must(session.NewSession(cfg))
s3.New(sess)
How can I check if the AWS session I created from go-sdk has expired or not.
Is there any fixed time for session expiry or what happens when I use the expired session to make a request to AWS via sdk.
Is there any specific return code or error that I should look for?
I have gone through a similar thread Does AWS SDK handles session expiry automatically?
But this doesn't have the solution I need.
My use case is that I create a session at the beginning and use it at 24 hours interval, So I want to make sure that I can find out that the session has expired and I can recreate the session and continue with my task.
There is no real answer to this because AWS documentation is not clear about session expiry, it only mentions the credentials expiry and interval to refresh them. It would be a good practice to recreate a session whenever a failure happens while using the session.
Do I have to Define Session and S3 as a package variable or Create instance in every api request.sample code as following.thanks a lot.
var AWSSession *session.Session
var S3Client *s3.S3
func InitAws() error {
log.Info("InitAws")
AWSSession, err := session.NewSession(&aws.Config{
Region: aws.String("XXXX"),
Credentials: credentials.NewStaticCredentials( "XX","XX",""), //just for test
})
if err != nil {
return err
}
log.Info("InitAws S3 Client")
S3Client = s3.New(AWSSession)
log.Info("InitAws end")
return nil
}
The package documentation says:
Sessions are safe to use concurrently as long as the Session is not being modified. Sessions should be cached when possible, because creating a new Session will load all configuration values from the environment, and config files each time the Session is created. Sharing the Session value across all of your service clients will ensure the configuration is loaded the fewest number of times possible.
Do create a single session and reuse it.
Whether you should store that single session in a package-level variable or pass it through function arguments is a separate question with opinionated answers.
I am having a very peculiar issue regarding User creation in AWS Cognito.
When I am in the same region (e.g US-East-AWS and Creating User from NA) then it is working like a charm. But when someone is in other region (e.g US-EAST-AWS and creating user from EU with a slow internet connection) then I am getting UserNotFoundException.
Below is the how I am handling User creation in my Lambda function (JS)
await cognitoidentityserviceprovider.adminCreateUser(params).promise()
if (customer.MFA === true) {
await toggleCognitoUsersMFA(jsonBody.email, true)
}
Check your MessageAction value in your params.
If the value is set to RESEND, and you're creating a new user, you'll get this User Not Found message.
The current AWS docs indicate that either a RESEND or SUPPRESS value is required, this is not true, you can actually omit the value.
How can i send an email/trigger a lambda function when a new user registers?
Under "edit identity pool" i only found a sync trigger.
If i understand correctly: This one is triggered every time a user syncs his data...
Is there any way to trigger a lambda function only for the "initial" sync or when a certain dataset is created for the user?
Edit:
To be more specific: I do create the user via lambdas using the JS SDK. I use developer authentication with my own oauth2 flow. I don't know how to distinguish between a user granting access e.g. via Google the first time from someone doing this the second time. The json with the access code seams the same to me... Maybe I am mistaken.
Also using the getOpenIdTokenForDeveloperIdentity call I don't know how to distinguish between an ID that is new to cognito from one cognito already knows.
Edit 2:
To be even more precise:
I am building on this project: https://github.com/laardee/serverless-authentication-boilerplate/blob/master/authentication/lib/storage/usersStorage.js
here is how i do save the User to cognito at the moment.
I do run this code for first time users as well as nth time users. My problem is that i dont know how to distinguish...
const saveCognito = (profile) => new Promise((resolve, reject) => {
if (profile) {
cognitoidentity.getOpenIdTokenForDeveloperIdentity({
IdentityPoolId: process.env.COGNITO_IDENTITY_POOL_ID,
Logins: {
// profile.userId = encrypted id of the e.g. google oauth2 id
[process.env.COGNITO_PROVIDER_NAME]: profile.userId
}
}, (err, dat) => {
if (err) {
reject(err);
} else {
var list_params = {
DatasetName: 'user-data', /* dataset name */
IdentityId: dat.IdentityId, /* cognito id */
IdentityPoolId: process.env.COGNITO_IDENTITY_POOL_ID
};
cognitosync.listRecords(list_params, function(err, data) {
if (err) {
reject(err); // an error occurred
} else {
var RecordPatches = //[Parts of the i want to write to the user]
// SyncSessionToken is returned by the cognitosync.listRecords call
list_params["SyncSessionToken"] = data.SyncSessionToken;
list_params["RecordPatches"] = RecordPatches;
cognitosync.updateRecords(list_params, function(err, update_data) {
if (err){
reject(err);
} else {
resolve();
}
});
}
});
}
});
} else {
reject('Invalid profile');
}
});
So this is something which is not currently supported in Cognito out of the box. You are correct in saying that the only built in Cognito Event that will trigger a Lambda Function is the "Sync Trigger" Event. This Sync event is fired every time that a Cognito IdentityId Synchronizes some of their data to the Cognito Sync cloud data store.
This event is unrelated to the creation of a new IdentityId by Cognito Federated Identity.
You could in theory:
Run a list-identities call on the IdentityPool, before the user logs
in.
Login the user. Check whether the IdentityId which has been given to the user is present in the list you retrieved before they logged
in. This would tell you whether or not the identity that they were
given existed before this login.
Based on this information you could
make a decision whether or not to programatically call the Lambda
Function from your application.
The setup of the above would be complex, as for security reasons you would need to maintain this service, server-side. The list-identities call requires AWS credentials to call. And I doubt you'd want to include permissions for that call in your IAM policy for unauthenticated users.
Aside from the above there is not much you can do at the moment.
In order to do this, you would need to setup a DynamoDB table (or some similar low latency datastore) where you could maintain the state of the IdentityId list, and then query this service/store whenever you login a user to compare new logins to the pre-existing list.
If this is critical to your use case I would suggest heading over to AWS Support, and create a case where you can log this as a feature request.
https://aws.amazon.com/premiumsupport/