AWS Cognito cookie storage - cookies

I'm trying to set up Cognito to use cookies instead of localStorage for credentials so that I can keep the user logged in between domains, e.g. x.foo.com and y.foo.com. The first step is to get it working on localhost but I'm stuck.
The documentation shows a simple config change should do the trick?
The following debug messages are comitted to the console:
[DEBUG] 37:08.223 AuthClass
Object { idToken: {…}, refreshToken: {…}, accessToken: {…}, clockDrift: 0 }
ConsoleLogger.js:87
[DEBUG] 37:08.228 Credentials - No Cache module registered in Amplify ConsoleLogger.js:84
[DEBUG] 37:08.230 Credentials - set credentials from session ConsoleLogger.js:84
[DEBUG] 37:08.230 Credentials - No Cognito Federated Identity pool provided ConsoleLogger.js:84
[DEBUG] 37:08.230 AuthClass - cannot get cognito credentials No Cognito Federated Identity pool provided ConsoleLogger.js:94
[DEBUG] 37:08.231 AuthClass - Failed to get user from user pool ConsoleLogger.js:84
[ERROR] 37:08.232 AuthClass - Failed to get the signed in user No current user
It seems when you specify the cookieStorage config you need to manually apply a cache instance? How do I do that and will it solve the problem?

This config works:
{
region: 'eu-west-1',
userPoolId: 'eu-west-1_XXXXXX',
userPoolWebClientId: 'XXXXXX',
mandatorySignIn: false,
cookieStorage: {
domain: 'localhost',
secure: false,
path: '/',
expires: 365,
},
}
In particular, secure must be false for localhost unless you are using https (Firefox ignores this for localhost, but Chrome and Safari don't).

Related

Amazon Cognito: add custom parameters to authorize URL when using OIDC identity provider

As stated in the title, I would like to add a custom parameter to the /authorize URL to which Cognito redirects when working with a OIDC User Pool Identity Provider (in my case https://example.xx.auth0.com/authorize).
I found out you can specify an authorize URL through cloudformation but it cannot contain query parameters.
More details:
Cognito is configured through Serverless (which uses Cloudformation under the hood):
Auth0IdentityProvider:
Type: AWS::Cognito::UserPoolIdentityProvider
Properties:
UserPoolId:
Ref: CognitoUserPool
ProviderType: "OIDC"
ProviderName: "Auth0"
ProviderDetails:
client_id: "xxxx"
client_secret: "xxxx"
attributes_request_method: "GET"
oidc_issuer: "https://xxxx.xx.auth0.com"
authorize_scopes: "openid profile email"
AttributeMapping:
email: "email"
When navigating to the Cognito hosted UI and selecting the Auth0 provider it redirects to the /authorize Cognito endpoint which in turn redirects to the /authorize Auth0 endpoint.
I need to add the connection parameter to Auth0's /authorize in order to bypass its UI and go straight to the social login but I haven't been able to find a way to do so.
Turns out that when configuring your Auth0 client you can specify the connection parameter and Auth0 will skip its UI for you, but it will only do that if the configured redirect_uri does not point to localhost.
auth0 = await createAuth0Client({
redirect_uri: window.location.origin,
scope: "openid profile email offline_access",
connection: "linkedin",
});

Not getting correct permission as per defined in scope in oAuth2 access token when trying to sign in using google

I am trying to sign in with google using AWS Cognito federation inside the user pool. My main goal is to get an access token and refresh token to call Google APIs on my server. I added the required scope and got permission as well, but the access token I am getting is not containing that extra permission in my case that's google calendar, and when I am trying to access calendar API getting a 401 Unauthorized error even.
I used Amplify library on React side to request tokens.
Amplify config:
Amplify.configure({
Auth: {
// REQUIRED - Amazon Cognito Region
region: 'us-east-2',
// OPTIONAL - Amazon Cognito User Pool ID
userPoolId: 'us-east-2_xxxxxxx',
// OPTIONAL - Amazon Cognito Web Client ID (26-char alphanumeric string)
userPoolWebClientId: 'xxxxxxxxxxxxxxxxxx',
// OPTIONAL - Hosted UI configuration
oauth: {
domain: 'calendar-google.auth.us-east-2.amazoncognito.com',
scope: [
'phone',
'email',
'profile',
'openid',
'aws.cognito.signin.user.admin',
],
redirectSignIn: 'http://localhost:3000/',
redirectSignOut: 'http://localhost:3000/',
responseType: 'code',
},
},
});
Scope used in cognito config
profile email openid https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar
enter image description here

AWS Cognito created by Serverless doesn't send confirmation emails

I'm working on an serverless authentication and I've got a problem with sending confirmation email.
When I create a user by AWS Console, the user is created and email with confirmation code is sent.
But when I do a request to API, user is created in Cognito but no email is sent.
What could be wrong with my code or request?
This is Cognito resource defined in serverless.yml:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: ${self:service}-${self:provider.stage}-user-pool
UsernameAttributes:
- email
AutoVerifiedAttributes:
- email
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
And it is request which I send to API:
POST / HTTP/1.1
Host: cognito-idp.eu-central-1.amazonaws.com
X-Amz-Target: AWSCognitoIdentityProviderService.SignUp
X-Amz-User-Agent: aws-amplify/0.1.x js amplify-authenticator
Content-Type: application/x-amz-json-1.1
Content-Length: ###
{
"ClientId": "####################",
"Username": "##########.##",
"Password": "####################",
"ValidationData": null
}
I resolved the problem by creating preSignUp lambda trigger with auto-confirming user:
const handler: PreSignUpTriggerHandler = async (event: PreSignUpTriggerEvent, context, callback) => {
event.response.autoConfirmUser = true;
callback(null, event);
};
Docs
AWS: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html
Serverless: https://www.serverless.com/framework/docs/providers/aws/events/cognito-user-pool/
I work with different framework (Amplify), but as the both frameworks use Cognito User pools, I guess the issue would be that you are missing "domain name".
In AWS Console, check your User Pool, then in (left panel) find "App integration" and then open and setup "Domain Name".
Again, I am just guessing (sorry, if my advice will not help), but I experienced similar problem.

API Gateway not returning Response

I was trying GCP API Gateway using firebase authentication. I can see my request has been processed from the logs and completed with response code 200. However, I am not getting the response back to my client. I am getting the response when I call the function directly. Am I missing something ?
API Config
swagger: "2.0"
info:
title: API Endpoints
description: API Endpoints
version: 1.0.1
schemes:
- https
produces:
- application/json
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/my-project"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "my-project"
paths:
/hello:
get:
summary: Test link
operationId: hello
x-google-backend:
address: https://us-central1-my-project.cloudfunctions.net/hello
security:
- firebase: []
responses:
"200":
description: A successful response
schema:
type: string
"403":
description: Failed to authenticate
Function
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
let message = req.query.message || req.body.message || 'Hello World!';
res.status(200).send(message);
};
Logs
Additional Query
Initially, I had my functions private and was getting 403. It gave me 200 once I added allUsers with Cloud Functions Invoker to the permissions to the function I am trying to invoke. So I am a bit confused here. Part of the reason I am using API gateway with firebase auth is to protect it against unauthorised calls. And for firebase auth to work, I had to add allUsers, making it public. My understanding was that the API gateway alone would be public while all the backend services that it invokes would be private. In this case, the function can be directly invoked by anyone, rendering API Gateway useless. How can I setup the backend to private and only respond to authenticated calls through API gateway ?
Additional Logs
{
insertId: "8c13b49c-2752-4216-8188-d445f4724ef14850908905639612439#a1"
jsonPayload: {
api_key_state: "NOT CHECKED"
api_method: "1.myapi.GenerateRTCToken"
api_name: "1.myapi"
api_version: "1.0.1"
client_ip: "999.999.999.999"
http_method: "GET"
http_response_code: 200
location: "us-central1"
log_message: "1.myapi.GenerateRTCToken is called"
producer_project_id: "myproject"
request_latency_in_ms: 161
request_size_in_bytes: 4020
response_size_in_bytes: 579
service_agent: "ESPv2/2.17.0"
service_config_id: "myapi-config"
timestamp: 1606313914.9804168
url: "/generateRTCToken"
}
logName: "projects/myproject/logs/myapi%2Fendpoints_log"
receiveTimestamp: "2020-11-25T14:18:36.521292489Z"
resource: {
labels: {
location: "us-central1"
method: "1.myapi.GenerateRTCToken"
project_id: "myproject"
service: "myapi"
version: "1.0.1"
}
type: "api"
}
severity: "INFO"
timestamp: "2020-11-25T14:18:34.980416865Z"
}
To call the Google Cloud Function from the Api-Gateway without making it public you can grant the service account used by the API-Gateway the role CloudFunctions Invoker
On Creating an API config (4) : you set a service Account, take note of this service account.
Once you have identified the service account you can grant it the Cloud Funtions Invoker role to the service account. For these you can follow these steps:
Go to IAM&Admin
Look for the service account, and click on the pencil next to it( If you don't see the service account click on Add and type the service account email)
For role Select Cloud Functions Invoker
Click on Save
This will grant the service account the permission to call the functions without having them public.
I have an similar issue with API Gateway throwing the same response validating a token against keycloak.
The issue was with the JWT, it was too long, we remove unused roles from the user and work perfectly.
Hope it helps.
Take a look at this document where it is explained in detail how to use Firebase to authenticate in API Gateway.
In general there are two conditions that we need to keep on mind in order to configure these services:
When your client application sends an HTTP request, the authorization header in the request must contain the following JWT claims:
iss (issuer)
sub (subject)
aud (audience)
iat (issued at)
exp (expiration time)
To support Firebase authentication, add the following to the security definition in your API config:
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
# Replace YOUR-PROJECT-ID with your project ID
x-google-issuer: "https://securetoken.google.com/YOUR-PROJECT-ID"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "YOUR-PROJECT-ID"

Can not connect to AWS Amplify PubSub -> Socket error:undefined

I've been trying out all ways to get the Amplify/PubSub working without any luck. It seems all the documentation are rather outdated.
Here is what I have done so far. Please note all hashes are made up ;-)
Created a fresh React Native app
Installed amplify packages
Installed Amplify CLI
Invoked $ amplify configure
Invoked $ amplify init
Invoked $ amplify add auth
Invoked $ amplify push, which created the aws-exports.js object
Created a super simple component
import React from 'react';
import { View } from 'react-native';
import { withAuthenticator } from 'aws-amplify-react-native';
import Amplify, { Auth, PubSub } from 'aws-amplify';
import { AWSIoTProvider } from '#aws-amplify/pubsub/lib/Providers';
import awsmobile from './aws-exports';
Amplify.configure({
Auth: {
mandatorySignIn: true,
region: awsmobile.aws_cognito_region,
userPoolId: awsmobile.aws_user_pools_id,
identityPoolId: awsmobile.aws_cognito_identity_pool_id,
userPoolWebClientId: awsmobile.aws_user_pools_web_client_id,
},
Analytics: {
disabled: true,
},
});
Amplify.addPluggable(
new AWSIoTProvider({
aws_pubsub_region: 'ap-southeast-2',
aws_pubsub_endpoint:
'wss://a123456789d-ats.iot.ap-southeast-2.amazonaws.com/mqtt',
})
);
Amplify.Logger.LOG_LEVEL = 'DEBUG';
class App extends PureComponent {
componentDidMount() {
if (this.props.authState === 'signedIn') {
Auth.currentCredentials().then((creds) => {
// get the principal that needs to be attached to policy
console.log('principal to be attached', creds.identityId)
PubSub.subscribe('topic_1').subscribe({
next: (data) => console.log(JSON.stringify(data, null, 2)),
error: (msg) => console.log('ERROR: ', msg.error),
close: () => console.log('Done'),
});
});
}
}
render() {
return (
<View/>
);
}
}
export default withAuthenticator(App);
I attached AWS root certificate to my iPhone (see below)
10.Create a IAM policy for AWS IoT
IoTAppPolicy
iot:*
arn:aws:iot:ap-southeast-2:1234567890:*
11.Attach the principal I got from Auth.currentCredentials to the policy
aws iot attach-principal-policy --policy-name IoTAppPolicy --principal ap-southeast-2:db1234bc-5678-90123-4567-89ae0e123b4
12.Attach Policies to Auth Role
AWSIoTDataAccess
AWSIoTConfigAccess
Yet, when I run the app I get the following error log
[DEBUG] 51:42.745 SignIn - Sign In for test#test.com
[DEBUG] 51:44.616 AuthClass CognitoUserSession {idToken: CognitoIdToken, refreshToken: CognitoRefreshToken, accessToken: CognitoAccessToken, clockDrift: 0}
[DEBUG] 51:44.616 Credentials - set credentials from session
[DEBUG] 51:46.247 Credentials - Load credentials successfully
[DEBUG] 51:46.248 AuthClass - succeed to get cognito credentials
DEBUG] 51:47.150 Hub - Dispatching to auth with {event: "signIn", data: CognitoUser, message: "A user 2d...de has been signed in"}
[DEBUG] 51:47.151 SignIn CognitoUser {username: "2d...de", pool: CognitoUserPool, Session: null, client: Client, signInUserSession: CognitoUserSession, …}
[DEBUG] 51:47.152 AuthClass - Getting the session from this user: CognitoUser {username: "2d...de", pool: CognitoUserPool, Session: null, client: Client, signInUserSession: CognitoUserSession, …}
[DEBUG] 51:47.152 AuthClass - Succeed to get the user session CognitoUserSession {idToken: CognitoIdToken, refreshToken: CognitoRefreshToken, accessToken: CognitoAccessToken, clockDrift: 1}
[DEBUG] 51:47.574 AuthPiece - verified user attributes {verified: {…}, unverified: {…}}
[INFO] 51:47.575 Authenticator - Inside handleStateChange method current authState: signIn
[DEBUG] 51:47.575 VerifyContact - no unverified contact
[INFO] 51:47.578 Authenticator - authState has been updated to signedIn
[DEBUG] 51:47.581 AuthClass - getting current credentials
[DEBUG] 51:47.582 Credentials - getting credentials
[DEBUG] 51:47.582 Credentials - picking up credentials
[DEBUG] 51:47.582 Credentials - getting new cred promise
[DEBUG] 51:47.582 Credentials - checking if credentials exists and not expired
[DEBUG] 51:47.583 Credentials - credentials not changed and not expired, directly return
[DEBUG] 51:47.583 AnalyticsClass - Analytics has been disabled
[DEBUG] 51:47.584 PubSub - subscribe options undefined
[DEBUG] 51:47.584 MqttOverWSProvider - Subscribing to topic(s) topic1
[DEBUG] 51:47.584 Credentials - getting credentials
[DEBUG] 51:47.584 Credentials - picking up credentials
[DEBUG] 51:47.584 Credentials - getting new cred promise
[DEBUG] 51:47.585 Credentials - checking if credentials exists and not expired
[DEBUG] 51:47.585 Credentials - are these credentials expired?
[DEBUG] 51:47.585 Credentials - credentials not changed and not expired, directly return
[DEBUG] 51:47.586 Signer {region: "ap-southeast-2", service: "iotdevicegateway"}
[DEBUG] 51:47.590 MqttOverWSProvider - Creating new MQTT client cca4e07f-a15a-46ce-904d-483a83162018
[WARN] 52:50.152 MqttOverWSProvider - cca4e07f-a15a-46ce-904d-483a83162018 {
"errorCode": 7,
"errorMessage": "AMQJS0007E Socket error:undefined.",
"uri": "wss://a123456789d.iot.ap-southeast-2.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAU7VGXF6UOWZIUFOM%2F20200502%2Fap-southeast-2%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20200502T055147Z&X-Amz-SignedHeaders=host&X-Amz-Signature=010d8..7dba&X-Amz-Security-Token=IQ..lk%3D"
}
ERROR: Disconnected, error code: 7
[DEBUG] 52:50.153 MqttOverWSProvider - Unsubscribing from topic(s) topic1
Any idea why I can't connect to the topic?
Who would have thought that a package was missing.
After installing
yarn add amazon-cognito-identity-js
everything worked fine on my physical iPhone.
I went to the AWS IoT Core page and published a test message from there and voilà I get the following object back
{
"message": "Hello from AWS IoT console"
}