Resource not Found userKey Google Directory API using Service Account - google-admin-sdk

Here is my credential built using service account,
GoogleCredential credential = new GoogleCredential.Builder()
.setJsonFactory(jsonFactory).setTransport(httpTransport)
.setServiceAccountId("xxxxxxx#developer.gserviceaccount.com")
.setServiceAccountPrivateKeyFromP12File(new File("key.p12"))
.setServiceAccountScopes(Collections.singleton(DirectoryScopes.ADMIN_DIRECTORY_USER))
.build();
And all I am trying is to get a user, where User is com.google.api.services.admin.directory.model.User
User u = null;
try {
u = service.users().get("test#gmail.com").execute();
} catch (Exception e) {
e.printStackTrace();
}
return u.toPrettyString();
}
response returns with code 404 Resource not found?? Is it user need to be created? how to access any gmail user?

Assuming you've already granted your service account domain-wide delegation, you'll need to have it impersonate an admin account using .setServiceAccountUser("admin#domain.com") as shown in the service account documentation. Service accounts are not themselves domain admins, so they need to impersonate one in order to use the Admin SDK APIs.

Related

Fetch signedJwt token for google service account

I am trying to generate signedJWT token for google service account
now = int(time.time())
expires = now + 900 # 15 mins in seconds, can't be longer.
payload = {
'iat': now,
'exp': expires,
'sub': 'somekey#someproject.iam.gserviceaccount.com',
'aud': 'aud'
}
body = {'payload': json.dumps(payload)}
name = 'projects/someproject/serviceAccounts/somekey#someproject.iam.gserviceaccount.com'
iam = googleapiclient.discovery.build('iam', 'v1', credentials=credentials)
request = iam.projects().serviceAccounts().signJwt(name=name, body=body)
resp = request.execute()
jwt = resp['signedJwt']
The problem I am facing is regarding credentials
If I use
credentials = service_account.Credentials.from_service_account_info(gcp_json_credentials_dict)
works fine.
But I am trying to use default service account
credentials, your_project_id = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
Getting following error -
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://iam.googleapis.com/v1/projects/someproject/serviceAccounts/somekey#someproject.iam.gserviceaccount.com:signJwt?alt=json returned "Permission iam.serviceAccounts.signJwt is required to perform this operation on service account
First, I will try to explain why you are getting the error:
permission iam.serviceAccounts.signJwt is required to perform this operation on service account.
A) Your code is running on a compute service using the default service account. I will call this Identity-1.
B) Your code is impersonating the identity somekey#someproject.iam.gserviceaccount.com. I will call this Identity-2.
Your code needs permissions to use the credentials of Identity-1 and the rights to impersonate Identity-2.
Part 1:
When you use the default service account for a compute service or assign a service account to a compute service, two forms of permission control are in effect. The master control is the IAM roles assigned to the service account. The limiter is the ** OAuth scopes** set for the compute service.
The OAuth scopes do not grant permissions, the OAuth scopes limit permissions already granted to the service account via IAM roles. This is an important point that I see so many configure incorrectly. I recommend using the compute engine Access scopes set to Allow full access to all Cloud APIs. Then modify/manage the IAM roles assigned to the service account assigned to Compute Engine.
Part 2:
In order to impersonate another identity, in your case Identity-1 is impersonating Identity-2, your code must have the right to do so. There are two types of identities that can be impersonated: a) service accounts; b) user identities. In your case, you are impersonating another service account.
If you are impersonating a service account, that requires granting the correct IAM permission via an IAM role on Identity-2 with the member set to Identity-1. Think of it this way: Identity-2 must grant permission to Identity-1.
If you are impersonating a user identity, that requires setting up Google Workspace Domain-Wide Delegation of Authority. The account that you are impersonating must be managed by Google Workspace. Refer to Google Workspace Domain-Wide Delegation of Authority is set up user account impersonation which is not required in your case.
Now you might wonder, which identity needs the right to impersonate? The identity that the JWT represents. That identity is declared by the JWT claim iss. Your JWT does not include an iss claim. For more details an example see this link. The identity that you are impersonating is specified by the claim sub. In OAuth speak iss is impersonating sub.
Part 3:
You must also configure your Google Cloud Project to support your objective. This requires enabling two APIs:
iamcredentials.googleapis.com
cloudresourcemanager.googleapis.com
Solution:
Enable the required APIs:
gcloud services enable iamcredentials.googleapis.com
gcloud services enable cloudresourcemanager.googleapis.com
Set the Compute Engine Access Scopes to Allow full access to all Cloud APIs. This requires shutting down the VM and editing the VM configuration.
Add the iss claim to the JWT that you are creating with the value Identity-1.
Grant an IAM Role containing the permission iam.serviceAccounts.signJwt. A good role to use is Service Account Token Creator to Identity-2. See this link for more details.
Example command:
gcloud iam service-accounts add-iam-policy-binding [Identity-2] \
--member serviceAccount:[Identity-1\ \
--role roles/iam.serviceAccountTokenCreator

AWS Pinpoint updating user attributes when unauthenticated - security issue?

I'm looking at using AWS Pinpoint to send push notifications to my react native app. However it seems that unauthenticated users are able to update user attributes for any user they wish, ie there is no access control. I'm new to mobile development, but isn't putting stuff like that into the frontend a security issue? If it were a web application, people would be able to inspect network calls to obtain credentials and make any call they wish to updateEndpoint. Is this not applicable to mobile apps or am I misunderstanding something?
Details:
There's a step in the setup that says Edit the IAM policy document for unauthenticated identities to allow permissions for the mobiletargeting:PutEvents and mobiletargeting:UpdateEndpoint actions
And react native code snippet provided goes as follows:
import Analytics from '#aws-amplify/analytics';
import Auth from '#aws-amplify/auth';';
const amplifyConfig = {
Auth: {
identityPoolId: 'COGNITO_IDENTITY_POOL_ID',
region: 'ap-south-1'
}
}
//Initialize Amplify
Auth.configure(amplifyConfig);
const analyticsConfig = {
AWSPinpoint: {
// Amazon Pinpoint App Client ID
appId: 'cd73a57d200e49e2bc4b97d6ebf63cd4',
// Amazon service region
region: 'ap-south-1',
mandatorySignIn: false,
}
}
Analytics.configure(analyticsConfig)
Analytics.updateEndpoint({
attributes: {
interests: ['science', 'politics', 'travel'],
//..
},
userId: 'UserIdValue',
userAttributes: {
username: 'ilovethecloud'
}
});
I'm not sure if this will help,
But you have 2 IAM policies (in Cognito Identity-pool), 1 for authenticated users and 1 for unauth. users.
You should restrict the IAM policy for unauth users, so they can't edit other users info.
Also, the credentials that you are given by Cognito, are temporal, they expire (and get renewed by your react-native app), so in that way you are safe.

AWS Amplify 'currentUserCredentials()' returns unexpected authenticated value, bug?

When calling Auth.currentUserCredentials() after Auth.signIn() I get valid valid credentials, but for an unauthenticated user, why?
Code:
async signIn({ dispatch, state }, { email, password }) {
try {
const user = await Auth.signIn(email, password);
console.log("User state after login")
const userCredentialsAfterLogin = await Auth.currentUserCredentials();
console.log(userCredentialsAfterLogin)
} catch (error) {
console.log("error")
console.log(error)
return
}
await dispatch('fetchUser')
},
Expected behaviour:
After signing in with a valid user, Auth.currentUserCredentials() should return an authenticated set of Credentials.
Actual behaviour:
Auth.currentUserCredentials() returns an unauthenticated user with the authenticated property set to false and a 400 error, "ResourceNotFoundException: IdentityPool 'eu-west-1:62dab5ed-5a84-4064-a7a2-87d1d0df511b'
System:
authenticationFlowType: "USER_SRP_AUTH"
Versions: "aws-amplify": "^3.3.14", "aws-amplify-vue": "^2.1.4", amplify version 4.42.0
config
{
"authSelections": "userPoolOnly",
"resourceName": "testapp89e81d50",
"serviceType": "imported",
"region": "eu-west-1"
}
I understand where you're coming from, and honestly I can't really show you clear documentation that exactly states why this won't work. AWS documentation on Cognito and Amplify is difficult to piece together, both because the Amplify framework still uses an old library under the hood ('amazon-cognito-identity-js') and Cognito is the name for both a connect-login-with-IAM and signup/signin-as-a-service offering. Cognito is super powerful and rock solid in terms of security if done right, but the setup is a bit of a pain.
There's a bit of documentation, e.g. the API docs of Amplify Auth here. You can see there that currentCredentials / currentUserCredentials gives you some object which contains among other things an 'identitiId'. Credentials, in the Amplify Auth framework, refer to AWS IAM credentials that refer to an Cognito Identity.
You seem to be using Amplify to login to a Cognito User Pool, using email/password. A Cognito User Pool can be connected to a Cognito Identify pool, to 'exchange' a Cognito JWT token for some credentials that can be used to use AWS resources (IAM credentials). This is not needed to have a normal sign in / sign up flow working though.
So, the question is: what do you want?
Do you want to know about the currently logged in Cognito User Pool user, e.g. his email, JWT token and other fields that are stored in the JWT token? Use 'currentUserInfo' or 'currentUserSession'
Do you actually want to have some IAM credentials to invoke AWS resources? Make sure to create and connect your Cognito User Pool with a Cognito Identity Pool and configure your Identity Pool id in your frontend settings. If you've done that, you should be able to use 'currentCredentials'.

AWS Role vs. Keys

AWS Roles are meant for services which requires access to AWS Services e.g. S3 etc. using temporary credentials. These are done using STS. This is useful when a user/application from one account needs access to a different account-owned resources on a temporary-basis.
However, STS will only issue a temporary credentials when the credentials are passed using Profile properties. At least that's what the code provided by AWS implies anyway
import com.amazonaws.AmazonServiceException;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
import com.amazonaws.services.securitytoken.model.Credentials;
public class MakingRequestsWithIAMTempCredentials {
public static void main(String[] args) {
String clientRegion = "*** Client region ***";
String roleARN = "*** ARN for role to be assumed ***";
String roleSessionName = "*** Role session name ***";
String bucketName = "*** Bucket name ***";
try {
// Creating the STS client is part of your trusted code. It has
// the security credentials you use to obtain temporary security credentials.
AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(new ProfileCredentialsProvider())
.withRegion(clientRegion)
.build();
// Obtain credentials for the IAM role. Note that you cannot assume the role of an AWS root account;
// Amazon S3 will deny access. You must use credentials for an IAM user or an IAM role.
AssumeRoleRequest roleRequest = new AssumeRoleRequest()
.withRoleArn(roleARN)
.withRoleSessionName(roleSessionName);
AssumeRoleResult roleResponse = stsClient.assumeRole(roleRequest);
Credentials sessionCredentials = roleResponse.getCredentials();
// Create a BasicSessionCredentials object that contains the credentials you just retrieved.
BasicSessionCredentials awsCredentials = new BasicSessionCredentials(
sessionCredentials.getAccessKeyId(),
sessionCredentials.getSecretAccessKey(),
sessionCredentials.getSessionToken());
// Provide temporary security credentials so that the Amazon S3 client
// can send authenticated requests to Amazon S3. You create the client
// using the sessionCredentials object.
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.withRegion(clientRegion)
.build();
// Verify that assuming the role worked and the permissions are set correctly
// by getting a set of object keys from the bucket.
ObjectListing objects = s3Client.listObjects(bucketName);
System.out.println("No. of Objects: " + objects.getObjectSummaries().size());
}
catch(AmazonServiceException e) {
// The call was transmitted successfully, but Amazon S3 couldn't process
// it, so it returned an error response.
e.printStackTrace();
}
catch(SdkClientException e) {
// Amazon S3 couldn't be contacted for a response, or the client
// couldn't parse the response from Amazon S3.
e.printStackTrace();
}
}
}
The above code will only work without providing some credentials. So my question is, how is Role useful here when I can just simply use access/secret key ?
Exactly the point you mentioned that the credentials are temporary is one of the many reasons why IAM roles are the recommended approach.
A role can be applied to AWS services as well as resources, for example an EC2 instance can have a role attached with AWS automatically rotating these. Additionally you can use STS to assume a role as role, this can be assumed from an IAM user, a role or a federated user.
You should try to avoid using IAM users where possible, there are some usecases such as signed URLs (where you would like it to last more than a few hours) as well as in an on-premise location. If you must use an IAM key you should make sure to rotate the key frequently.
For more information take a look at the IAM Identities (users, groups, and roles) and Security best practices in IAM pages.

Getting AWS IAM user by access key id using sdk

I'm trying to verify that an IAM user exists with an access key using the AWS SDK(js).
I am trying to build an access control module for an API. I can't deploy it on AWS. Instead of trying to build the whole thing, I want to handle user management using IAM but will need to build a custom module to check if current IAM user has access to resources.
I've checked the docs and looks like you can only get user by username. I thought maybe I can list the users and filter the user array by access key but obviously the list does not have access key info.
The AWS Identity and Access Management (IAM) service is designed specifically for granting access to AWS resources. It is not designed as an authentication system for applications.
A more appropriate product would be Amazon Cognito:
Amazon Cognito lets you easily add user sign-up and sign-in and manage permissions for your mobile and web apps. You can create your own user directory within Amazon Cognito. You can also choose to authenticate users through social identity providers such as Facebook, or Amazon; with SAML identity solutions; or by using your own identity system. In addition, Amazon Cognito enables you to save data locally on users' devices, allowing your applications to work even when the devices are offline. You can then synchronize data across users' devices so that their app experience remains consistent regardless of the device they use.
With Amazon Cognito, you can focus on creating great app experiences instead of worrying about building, securing, and scaling a solution to handle user management, authentication, and synchronization across devices.
You can check whether the user exist or not by IAM User's credentials using AWS CLI/SDK
Attach iam:GetUser and iam:SimulatePrincipalPolicy policies to your IAM user.
Here iam:GetUser will be used to check the user existence and iam:SimulatePrincipalPolicy will be used to check the resource access.
1. Check existence:
You can use getUser() function of AWS-IAM to verify whether user exist or not
const iam = new AWS.IAM({
// Iam User access and secret Key
})
iam.getUser({}, (err,data)=>{
if(err)
console.log("User not exist");
else
console.log("User exist ", data);
})
if user exists:=>
`{
"User": {
"Path": "/",
"UserName": "userName",
"UserId": "AIDAY357ZXJ7ADSEWNGWA3",
"Arn": "arn:aws:iam::60977878822:user/userName", // required this in simulator
"CreateDate": "2020-08-05T14:48:49Z"
}
}`
2. For resource access you can use simulatePrincipalPolicy() function of AWS-IAM.
let params = {
PolicySourceArn = "Paste IAM user arn", // arn:aws:iam::60977878822:user/userName
ActionNames = ["ec2:RunInstances"]
}
iam.simulatePrincipalPolicy(params, (err, data)=> {
if(err)
console.log("Error", err);
else
console.log("Data ", data);
})
output:=>
`{
ResponseMetadata: { RequestId: '3e7cbc9a-ed7b-472a-b054-a6f3f37bf8c4' },
EvaluationResults:
[ { EvalActionName: 'iam:SimulatePrincipalPolicy',
EvalResourceName: '*',
**EvalDecision: 'allowed',** // check this
MatchedStatements: [Array],
MissingContextValues: [],
ResourceSpecificResults: [] },
],
IsTruncated: false
}`
If the EvalDecision is 'allowed' means your IAM User have the access to create new instances.