Adding a User Sign-in for Mobile App on a serverless backend on AWS - aws-sdk

i am creating a mobile app for multiple platforms (iOS, Android, PWA (Web)) and have my backend build with the serverless framework on AWS Lambda, using API Gateway and DynamoDb.
My most recent goal is to add a user login using AWS Cognito, enabling my users to either login using their email and password, or a facebook login. I read multiple threads, tutorials and the official AWS Documentation but am super confused because of the mass of different information.
I want to integrate authorized API Endpoints which can only be called when the user is logged in.
Therefore my first question is:
1) Should i rather use AWS Cognito User Pools or AWS Cognito Identity Pools?
I also read that for user pools you can solely use the hosted UI for login, which is not a possibility for my app. I want to style my own login page.
As i could not find any sample code for the client side integration, i went with identity Pools for now, and created an endpoint in my serverless.yml which is specified the following:
functions:
xxx-auth:
handler: endpoints/xxx-auth.execute
events:
- http:
path: xxx-auth
method: put
cors: true
integration: lambda
authorizer: aws_iam
So my second question is:
2) How do i properly integrate the authorization for my Cognito Identity Pool or User Pool?
From another Thread i read that there is the possibility to generate an SDK for the API one created on API Gateway, which handles the signature procedures of the request for you. As i tried to do so in my iOS application, i first log the user in using the following code:
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .EUCentral1 ,
identityPoolId:"eu-central-1:XXXXXXXX" ,
identityProviderManager: CustomIdentityProvider(tokens: ["graph.facebook.com": FBSDKAccessToken.current().tokenString]))
let configuration = AWSServiceConfiguration(region: .EUCentral1, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
and then use the following created code to submit the PUT request:
public func XXXPut() -> AWSTask<AnyObject> {
let headerParameters = [
"Content-Type": "application/json",
"Accept": "application/json",
]
let queryParameters:[String:Any] = [:]
let pathParameters:[String:Any] = [:]
return self.invokeHTTPRequest("PUT", urlString: "/XXX", pathParameters: pathParameters, queryParameters: queryParameters, headerParameters: headerParameters, body: nil, responseClass: nil)
}
But i cannot see any code that signs the HTTP request. Therefore the third question is:
3) How do i create the SDK that properly sends the signed HTTP request to my API Gateway backend?
I also tried the same for Cognito User Pools, using a simple "Authorization" header, but could not figure out a way to obtain the respective Authorization token on the iOS application without using any kind of hosted UI.
It would be very kind if someone could explain this jungle to me.

I figured it out by myself:
1) It depends. I am now using a combination of both for the following reason:
For a Facebook Login, we are using a Cognito Identity Pool, as the Cognito UserPool does not support a Facebook Login outside of the hosted UI. For Email signup and login we use a UserPool inside the Cognito Identity Pool.
2) The code snippet i posted was correct.
3) The code snippet was also correct. My mistake was that i did not correctly display and inspect the reply of my API and therefore thought the request was not authorized, but it was.

Related

How to get user data in AWS?

I'm trying to make a simple (React) website where a user signs in and it gets their data from a MySQL database and displays it in a table on the website. The flow is like this:
Home Page -> Sign In -> App
I want to show the user's data that is stored in a AWS RDS (MySQL) when they log into the App.
I have the domain and static hosting set up. I have a user pool set up in AWS Cognito. I set up an API Gateway with authorization and connected a basic lambda function to it. When the user signs in the are redirected to the App page with a JWT token in the header. I don't know where to go from here.
This is what I'm thinking:
User logs in, redirected to callback URL with JWT token in header.
Get the JWT token, decode it and get the user email
In my Lambda function, connect to the database instance (AWS RDS)
To query user data, select user data from a table whose name is the
users email
I don't know how to do any of the above though (apart from SQL queries). Are these even the correct steps?
How do I get the JWT token in my code? From what I know it has to be passed with every API call? I have my invoke URL from API Gateway, how do I send a request with the JWT token?
What would the database instance look like? Would it be a collection of tables that each represent a user?
How do I do this with and without AWS Amplify?
I've been trying for a few weeks but there's so much info and I feel like I'm over complicating something that is very simple to implement.
Another approach is to use the AWS SDK for JavaScript to query data on the backend and a React front end that displays the data.
This use case is located in the AWS Code Lib here:
Create an Aurora Serverless work item tracker
This example shows how to use the AWS SDK for JavaScript (v3) to create a web application that tracks work items in an Amazon Aurora database and emails reports by using Amazon Simple Email Service (Amazon SES). This example uses a front end built with React.js to interact with an Express Node.js backend.
Services used in this example
AWS Services used in this example:
Aurora
Amazon RDS
Amazon RDS Data Service
Amazon SES
Note that this example focuses on the AWS SDK, such as the RDSDataClient) and not stuff like JWT tokens, Amplify etc

Cloud Identity Platform make Custom SAML ACS Callback

I'm trying to make a custom SAML app to integrate with Google Workspace (i.e. so that if a person in the organization wants to access it, they could do so from the apps list on google.com).
Because Google Cloud Identity Platform only supports service provider-initiated login, this does not seem possible using the default callback URL they provide. I saw this answer to a similar question, and was hoping to implement something like this. However, the SAMLResponse coming in seems to be encrypted, and I don't know enough about the encryption process to know how to decrypt it (or if that's even possible).
I'm using a Cloud Function as my callback URL, and to be clear I'm trying to decrypt the res.body.SAMLResponse string:
exports.samlACSCallback = functions.https.onRequest(async (req, res) => {
console.log(req.body.SAMLResponse)
})
My best guess is that it's somehow related to the certificate that I had to copy from the Google Admin console to the Cloud Identity setup page?

Unable to login User ID to Web Messenger (Invalid Scope)

We're having issues initialising sessions in Smooch. We've successfully created a JWT token using the account level scope, and we've created a user (mycatalyst_test) using the API. When we use the API, we're able to access and query the user, create messages etc. correctly.
However, when we try to initialise the Smooch web messenger and log the user in:
Smooch.init({
appId: 'XXX',
userId: 'mycatalyst_test',
jwt: 'XXX',
embedded: true
});
We receive the error:
{"error":{"code":"forbidden","description":"Invalid scope"}}
We've checked the documentation and can't find any reference to the Invalid scope error or the Forbidden code in the Web Messenger documentation, and we're able to use the same JWT key to access the information through the API successfully, so we're not entirely sure what's going wrong.
We tried using the App scope rather than the Account scope to generate a new JWT key, but this also didn't work. We tried using the Smooch ID rather than the User ID, but this also didn't work.
We're at a bit of a loose end here, so would appreciate any pointers! (We're probably doing something stupid and obvious!).
See the documentation for authenticating users. When supplying a JWT to the login API, you must use a scope of appUser, otherwise you would be distributing JWTs to your users that give access to your whole account/app!
jwt.sign({
scope: 'appUser',
userId: userId
},
SECRET,
{
header: {
alg: 'HS256',
typ: 'JWT',
kid: KEY_ID
}
});
There is also this note in the authorization section of the REST API documentation:
An additional scope of appUser can also be used to authenticate users when using one of the Smooch native SDK integrations. For information on how and when to use this scope, see the guide for authenticating users.

How to Sign Into Specific Google Drive Account through Javascript Google Drive API

I am trying to run a script off of my Google Drive through the Javascript Google Drive API. This works fine, but only if I sign into my account on the popup that opens. I wish to sign into the same account every time and so was wondering if there was any way to automate the login of this so as to bypass users having to enter in that login information.
In short, you would have login at least once, everytime after the Google Identity Provider JSON Web Token expires. I am not sure how long this would be with the Goolge Drive API, but typically these tokens may be valid for anywhere from a single request to days long.
Here is the Documentation for the Google API OAuth2
https://developers.google.com/identity/protocols/OAuth2
Refresh the access token, if necessary.
Access tokens have limited lifetimes. If your application needs access
to a Google API beyond the lifetime of a single access token, it can
obtain a refresh token. A refresh token allows your application to
obtain new access tokens.
Note: Save refresh tokens in secure long-term storage and continue to
use them as long as they remain valid. Limits apply to the number of
refresh tokens that are issued per client-user combination, and per
user across all clients, and these limits are different. If your
application requests enough refresh tokens to go over one of the
limits, older refresh tokens stop working.
Google has provided a quickstart guide for implementing a user sign via Google Apis. Google uses the OAuth2 protocol in which you must register with Google as a Client application. Once registered as a Client application, you will be issued a Client ID, which you typically provide to your application in some form of application initialization.
Here is a link to their quickstart guide, which will help you get started:
https://developers.google.com/drive/v3/web/quickstart/js
Note that this is a basic example that does not demonstrate how you may approach persisting a JSON Web Token so that the user does not have to login on every request. I outline a simple approach of managing Authentication in JavaScript and Angular to get you moving in the right direction, but incomplete, direction.
For example, in Angular:
// Configures the required variables before Running an Instance of the App
angular.module("yourModuleName").config(configureApp);
AND
// Executed when the App Instance launches, allowing you to connect to Google APIs when the App starts
angular.module("yourModuleName").run(runApp);
Where configureApp and runApp are JS functions that handle application initialization in the AngularJS Framework. The code in the follow example would retrieve the Apps Google Client ID from their own App's REST API. This is just an example of where you could retrieve these credentials from storage, but most likely is not the most secure example:
var configureApp = function($http,$window) {
// Setup your CLIENT ID from your own REST API (or any other mechanism you may choose)
var httpPromise = $http.get("http://myApp.myDomain.com/config/googleClient");
// Handle the Response from the above GET Request
httpPromise.then(
// Handle Success
function(response) {
// Store the CLIENT ID in local storage for example
$window.localStorage.setItem("GOOGLE_API_CLIENT_ID", response.data.clientId);
// Setup the App Wide variables for Google API
// Client ID and API key from the Developer Console
var CLIENT_ID = response.data.clientId;
// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"];
// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';
// Do more initialization configuration
};
var runApp = function() {
// Initialize the API
gapi.client.init({
discoveryDocs: DISCOVERY_DOCS,
clientId: CLIENT_ID,
scope: SCOPES
}).then(function () {
// Listen for sign-in state changes.
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// Handle the initial sign-in state.
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
authorizeButton.onclick = handleAuthClick;
signoutButton.onclick = handleSignoutClick;
});
}
Which function to use with Angular would depend on the desired app lifecycle you need to target in an Angularjs app. This approach can be applied in other JS frameworks like React and Backbone.
To highlight another perspective from the documentation, updateSigninStatus would be a great place to capture the JSON Web Token returned by Google's Authorization request at which point you could store this token in the browser's window.localStorage for re-use.
You then could reuse the token whenever the Google API requires authentication. Tokens typically have an expiration. Until the token expires, you would be able to prevent the API from displaying a login modal.
This does mean you would still have to manage the logic behind the Authorization process using this approach, monitoring any response from Google requesting a token refresh or re-authentication.
Auth0 is a great Authentication and Authorization plugin available in many languages for connecting with Google and many other OAuth2 Identity Providers. The Google Drive API uses their own Identity Provider Service to confirm the Identity of your apps users in tandem with your registered app's Client ID.
Here are links that I found when implementing Authorization for a project that required me to implement Authorization using the Google Identity Provider:
https://jwt.io/
https://auth0.com/
Best practices for authentication and authorization in Angular without breaking RESTful principles?
https://thinkster.io/tutorials/angularjs-jwt-auth
You are saying that all users login to the same Google account?
In that case you have 2 options.
1/ write a server application that has a stored refresh token. Create an endpoint that allows an authenticated user to request an access token.
2/ embed a refresh token in your JavaScript, but make sure that only authenticated users can load the JS

Integration of Spring Oauth2 implementation with the HTML front end using javascript

I am implementing spring oauth2 for securing my rest api. Basically i am limiting the use of rest api to particular users rather then limiting to every users.
I had implemented the backend and secured my api using spring oauth2.
I am following this steps:
1)Send the GET request with the five parameters.
localhost:8080/SpringRestSecurityOauth/oauth/token?grant_type=password&client_id=Awyi123nasdk89&client_secret=asdj39m32##$s&username=rahul#gmail&password=rahul#9831
2) Server validates the user with the help of spring security and return the json response with access code.
{
"access_token": "22cb0d50-5bb9-463d-8c4a-8ddd680f553f",
"token_type": "bearer",
"refresh_token": "7ac7940a-d29d-4a4c-9a47-25a2167c8c49",
"expires_in": 119
}
3)I access protected resources by passing this access token as a parameter, the request goes something like this:
localhost:8080/SpringRestSecurityOauth/api/users/?access_token=8c191a0f-ebe8-42cb-bc18-8e80f2c4238e
4) In case the token is expired, user needs to get a new token using its refreshing token that was accepted in step(2). A new access token request after expiration looks something like this:
localhost:8080/SpringRestSecurityOauth/oauth/token?grant_type=refresh_token&client_id=restapp&client_secret=restapp&refresh_token=7ac7940a-d29d-4a4c-9a47-25a2167c8c49
All the above step are working fine. Now i need to implement this on my client side. So that a particular client can access this call. I am using HTML/CSS as client side technology.
How client can get the access token? Should it be stored in the browser local storage? Or it should be generated every time the rest call is been made?
Any example would help me to proceed further.
I'm implementing my project like you. I use angularjs and get the access token from response json then store it into cookies.
This link provide sample code for you: http://www.baeldung.com/rest-api-spring-oauth2-angularjs. (See Frontend - Password Flow).
Because refresh token should keep secret and the client is html app, you should see this link http://www.baeldung.com/spring-security-oauth2-refresh-token-angular-js for handling refresh token.
For html client, after obtaining access token using refresh token when access token is expired, I use http-auth-interceptor ([http]://witoldsz.github.io/angular-http-auth/) to retry all rest requests failed because of expired access token.
I'm sorry that I have not enough reputation to post more than 2 links.