GCIP - enable authorization code grant flow using OIDC based external provider - google-cloud-platform

Trying to configure GCIP with Salesforce Identity as IDP. Tried configuring OIDC based integration. Noticed that there is no field for providing (sfdc) client secret for OIDC based configuration. Also, the response_type=id_token is getting invoked from GCIP side. We want to use authorization code flow (response_type=code) to integrate with SFDC. Is it possible?

Code flow for OIDC providers is supported on the GCIP backend. It is just not yet exposed in the Cloud Console or the Admin SDKs.
Notice it is documented here in the REST API.
You will need to set {code: true}
Here is a snippet in Node.js (untested):
// https://cloud.google.com/identity-platform/docs/reference/rest/v2/projects.oauthIdpConfigs/patch
return new Promise((resolve, reject) => {
request({
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
url: `https://identitytoolkit.googleapis.com/admin/v2/projects` +
`/${projectId}/oauthIdpConfigs/${oidcProviderId}?updateMask=responseType`,
method: 'PATCH',
body: JSON.stringify({
responseType: {
idToken: true,
code: true,
}
}),
}, (error, response) => {
if (!error && response.statusCode === 200) {
resolve();
} else {
reject(error);
}
});
});
});

Related

AWS Enabling CORS for my API triggering a Lambda

I managed to create an AWS Lambda that does two things: writes on a dynamo DB and sends an SMS to a mobile number. Then I call this Lambda through a API Gateway POST call, and it works great from the Test section on AWS console but it gives error both on Postman and my own website. I inserted a callback to handle CORS on Lambda and deployed + enabled CORS on my API via console and deployed it but still get errors:
Errors via postman call: {
"message": "Internal server error"
}
Errors via my website (jquery ajax POST call): Lambda calling failed: {"readyState":4,"responseText":"{"message": "Internal server error"}","responseJSON":{"message":"Internal server error"},"status":500,"statusText":"error"}
This is my lambda code
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB();
const SNS = new AWS.SNS();
const tableName = "#####";
let params = {
PhoneNumber: 'mynumber####',
Message: 'Someone wrote!'
};
exports.handler = (event, context, callback) => {
dynamodb.putItem({
"TableName": tableName,
"Item" : {
"Id": {
N: event.Id
},
"Type": {
S: event.Type
}
}
}, function(err, data) {
if (err) {
console.log('Error putting item into dynamodb failed: '+err);
}
else {
console.log('Success in writing, now starting to send SMS');
return new Promise((resolve, reject) => {
SNS.publish(params, function(err, data) {
if(err) {
console.log("Error in sending sms alarm");
reject(err);
}
else {
console.log("SMS alarm sent!");
resolve(data);
}
})
})
}
});
callback(null, {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify('Hello from Lambda!'),
});
};
What am I doing wrong? I don't think permissions on lambda are the problem here, since testing it on console works both on writing on dynamo both sending the sms to my cellphone.
If I click from here on API Endpoint I get the same error {"message": "Internal server error"}
SOLUTION: instead of making an AWS HTTP API, make a AWS REST API, that is much more complex and offers more personalization for CORS, letting you set them and headers.

AWS API Gateway working from POSTMAN, but not from AWS Lambda function

I have an API gateway, which is working from the POSTMAN, using a valid access token, but when I call the same API gateway from inside Lambda function, I am getting 403 access denied error for the same access token. I am not sure where I am missing something. It's really weird, where everything from URL, API_KEY, access token, JSON body is the same, but worked differently in both services.
Please let me know if you have some pointers.
Here is the function that I am using for calling API Gateway -
return new Promise((resolve, reject) => {
var options = {
'method': 'POST',
'url': 'https://XXXXXXXX.execute-api.ap-south-1.amazonaws.com/dev/Patient',
'headers': {
'Content-Type': 'application/json',
'X-Api-Key': 'XXXXXXXX',
'Authorization': 'Bearer ' + accessToken
},
body: JSON.stringify({"resourceType":"Patient","active":true,"name":[{"family":"Smith","given":["Emily"]}],"gender":"female","birthDate":"1995-09-24","identifier":["1221212121"],"managingOrganization":{"reference":"Organization/2.16.840.1.113883.19.5","display":"Good Health Clinic"}})
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
});
Thanks

AWS api gateway working in browser but returning 502 in fiddler

I have a lambda function which would basically authenticate against the password stored in aws secret manager. The secret manager path would be the username and it will have the value for password. password will need to be passed in the header and username in the query. When I access the url https://{myawsurl}.execute-api.{region}.amazonaws.com/demo/{username} in a browser, I get the error password is missing in the header(which is expected). When I hit the url using fiddler I get 502 all the time.
My api gateway is simply a GET to the lambda function below:
const aws = require("aws-sdk");
const sm = new aws.SecretsManager({ region: 'us-east-1' })
const getSecrets = async (SecretId) => {
return await new Promise((resolve, reject) => {
sm.getSecretValue({ SecretId }, (err, result) => {
if (err) {
reject(err);
}
else {
resolve(JSON.parse(result.SecretString));
}
});
});
}
const main = async (event) => {
console.log("Event: ", event);
try {
const username = event.queryStringParameters ? event.queryStringParameters.username : (event.pathParameters ? event.pathParameters.username : null);
if (username === null || username === undefined || username.trim().length === 0) {
if (username === null || username === undefined || username.trim().length === 0) {
return {
statusCode: 400,
headers: {
"Content-Type": "application/json"
},
body: "username is missing in the url. Please add `/?username={username}` or `/{username}` in the url"
};
}
}
const password = event.headers ? event.headers.password : null;
if (password === null || password === undefined || password.trim().length === 0) {
return {
statusCode: 400,
headers: {
"Content-Type": "application/json"
},
body: "password is missing in the header"
};
}
const secrets = await getSecrets(username);
if (password !== secrets.password) {
return {
statusCode: 403,
headers: {
"Content-Type": "application/json"
},
body: "Incorrect username/password"
};
}
return {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: "User is Authenticated"
};
} catch (e) {
return {
statusCode: 404,
headers: {
"Content-Type": "application/json"
},
body: e.message
};
}
}
exports.handler = main;
My fiddler request is below:
GET https://{myawsurl}.execute-api.{region}.amazonaws.com/demo/{username} HTTP/1.1
password: MyTestPassword
I saw other posts where they mentioned about having a statusCode and body being a string. I have those but still getting error...
I added/removed the headers: { "Content-Type": "application/json"}, from the response and it made no difference..
EDIT: One another thing noticed is whenever I access the api gateway url via browser, it gets logged in my api's log group. But when it is accessed using fiddler it doesn't log. Not sure why...
EDIT: After the suggestion from #ArunK, I used Postman and I found it returns the expected response from the api gateway. I assume some settings in Fiddler may be causing this to happen..
Looks like the issue related to the TLS version supported by Fiddler. You need to include tls 1.0 and 1.2 since AWS API Gateway support these TLS Versions.
From the docs:
A security policy is a predefined combination of minimum TLS version
and cipher suite offered by Amazon API Gateway. You can choose either
a TLS version 1.2 or TLS version 1.0 security policy.
Go to Tools -> Options -> Https and verify the following exists under Protocols - <client>;ssl3;tls1.0;tls1.1;tls1.2
More about Fiddler and Modern TLS Versions.

Sending http request from aws lambda to google firebse funcitons

I have set up firebase functions to receive http requests and have verified that the same is working. Now im trying to send http request to firebase from aws lambda function. But there is no response either in aws lambda or in the firebase functions log. This is my aws lambda code:
const postData = JSON.stringify({
"queryresult" : {
"parameters": {
"on": "1",
"device": "1",
"off": ""
}
}
});
const options = {
hostname: 'https://<the firebase function endpoint>',
port: 443,
path: '',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = https.request(options, postData)
.then((response) => {
console.log(response);
})
.catch((err) => {
console.log(err);
});
// Write data to request body
req.write(postData);
req.end();
}
The promise part here is suppose to execute the console logs but it is not getting executed. Is there something that i'm missing here. The host is the URL that we obtain when we deploy a function. Or is there some firebase or aws related plan problem. I'am using the spark plan in firebase. Thankyou.

Firebase as Identity Provider with Cognito / AWS

I am having a hard time using Firebase as an Open ID Connect provider.
Can you please further describe the steps you have been through before and after to make this work?
For information, here is what I have done so far:
In AWS Console:
1 - Create an IAM Identity Provider ( OpenID Connect ) and used securetoken.google.com/<FIREBASE_PROJECT_ID> as an URL, <FIREBASE_PROJECT_ID>for Audience
2 - Checked the Thumbprint manually (it matches the one generated by AWS)
3 - Created a role with the permissions to access the desired services
4 - Created an Identity Pool in Cognito and selected my newly created role in the 'Authenticated role' Dropdown
5 - Selected my Identity Provider under the Authentication Providers > OpenID category (format is therefore): securetoken.google.com/<FIREBASE_PROJECT_ID>
In my code (I am using Vue.js) here are the logical steps I went through:
Import / setup AWS SDK
Invoke Firebase Auth service
Create a new CognitoIdentity
Use the getOpenIdTokenForDeveloperIdentity and push the tokenID received from Firebase
The issue is that I keep getting "Missing credentials in config" errors.
The code:
import axios from 'axios';
const AWS = require('aws-sdk');
AWS.config.region = 'eu-west-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'MY_COGNITO_POOL_ID',
});
export default {
name: 'My Vue.js component name',
data() {
return {
email: '',
password: '',
msg: '',
};
},
methods: {
submit() {
axios
.post(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=MY_KEY',
{
email: this.email,
password: password,
returnSecureToken: true,
},
)
.then((res) => {
// stores tokens locally
localStorage.setItem('jwt', JSON.stringify(res.data));
const cognitoidentity = new AWS.CognitoIdentity();
const params = {
IdentityPoolId: 'MY_COGNITO_POOL_ID',
Logins: {
'securetoken.google.com/<PROJECT_ID>': res.data.idToken,
},
IdentityId: null,
TokenDuration: 3600,
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params, (err, data) => {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
});
},
},
};
Here are the resources I have used so far while attempting to make this work:
http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
Using Firebase OpenID Connect provider as AWS IAM Identity Provider
https://github.com/aws/amazon-cognito-identity-js/blob/master/examples/babel-webpack/src/main.jsx
http://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetCredentialsForIdentity.html
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication/
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-2-developer-authenticated-identities/
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-3-roles-and-policies/
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-4-enhanced-flow/
The final code if that can be any help for anyone:
import axios from 'axios';
const AWS = require('aws-sdk');
const aws4 = require('aws4');
export default {
name: 'VUE_CPNT_NAME',
data() {
return {
email: '',
password: '',
msg: '',
idToken: '',
};
},
methods: {
submit() {
// Firebase SignIn API
// Doc: https://firebase.google.com/docs/reference/rest/auth/
axios
.post(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=[MY_KEY]',
{
email: this.email,
password: this.password,
returnSecureToken: true,
},
)
.then((res) => {
this.idToken = res.data.idToken;
localStorage.setItem('jwt', JSON.stringify(res.data));
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: {
'securetoken.google.com/<FIREBASE_PROJECT_ID>': res.data.idToken,
},
}, {
region: 'eu-west-1',
});
// AWS.config.crendentials.get() methods works as well
// or a call to cognitoidentity.getId() followed by a call to getCredentialsForIdentity()
// will achieve the same thing. Cool. But why!?
AWS.config.getCredentials((err) => {
if (err) {
console.log(err);
}
const request = {
host: 'API_GATEWAY_ENDPOINT.eu-west-1.amazonaws.com',
method: 'GET',
url: 'https://API_GATEWAY_ENDPOINT.eu-west-1.amazonaws.com/PATH',
path: '/API_ENDPOINT_PATH',
};
// Signing the requests to API Gateway when the Authorization is set AWS_IAM.
// Not required when Cognito User Pools are used
const signedRequest = aws4.sign(request,
{
secretAccessKey: AWS.config.credentials.secretAccessKey,
accessKeyId: AWS.config.credentials.accessKeyId,
sessionToken: AWS.config.credentials.sessionToken,
});
// removing the Host header to avoid errors in Chrome
delete signedRequest.headers.Host;
axios(signedRequest);
});
});
},
},
};
Try setting the login map i.e the firebase token in the CognitoIdentityCredentials object. See this doc.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'MY_COGNITO_POOL_ID',
Logins: {
'securetoken.google.com/':
}
});
Try calling get method on the credentials object before initializing the Cognito client. You can also use getCredentials instead.
If the above steps do not work & they should, pass the credentials as an option while initializing the Cognito client. See this doc for options available while using the CognitoIdentity constructor.
const cognitoidentity = new AWS.CognitoIdentity({credentials: AWS.config.credentials});
If you still receive the error, try logging credentials object in the console after calling the get() method. Ideally, it should have temporary credentials (accessKey, secretKey & sessionToken)