guywald / serverless-auth-msg-board

Example for authenticated message board backend with Serverless architecture on AWS. See https://github.com/guywald/serverless-auth-msg-board-unity3d-client for Unity 3D Client
MIT License
3 stars 1 forks source link

Question about Handler (Core vs ASP.NET) #1

Open ddough44 opened 7 years ago

ddough44 commented 7 years ago

hi @guywald , sorry if this is in the wrong place - I wasn't sure the best place to ask this question.

I am in the process of creating a Unity3d game using AWS as my backend and came across your post when googling for help with Facebook/Cognito/APIGateway integration. This example is CRAZY valuable. Thank you!

I had a question for you - your Handler is using APIGatewayProxyResponse (e.g. .NET Core). I know this is just an example, but I'm wondering why you choose this over just an ASP.NET Core Web API.

I am in the process of building a POC and fleshing out fully all the nuanced details of building the scalable game using AWS for the backend, and one of the things I noticed is that AWS allows me to either write a Lambda which takes the APIGatewayProxy (.NET Core), or actually just directly make use of Controllers and other MVC paradigms and it will automatically marshal that so I don't have to deal with the APIGatewayProxyResponse or use Tasks (ASP.NET Core).

I'm wondering why in your example you use .NET Core over ASP.NET Core? Is there an advantage you found, or pitfall with using ASP.NET Core with Unity?

It seems super badass to be able to run a local web server, test all my code, then push.. Plus, I keep all the nice MVC Semantics that I'm used to using in web apps...

Thank you for any insights!

Drew

guywald commented 7 years ago

Hi @ddough44,

Thanks for the question. Choosing C# for the lambda code wasn't done for a specific reason (could of used JS equally). Regarding using the ASP.NET Core Web API, at the time of writing the example, I hadn't thought of trying it, my goal was to make the signing (SigV4) process work. I've tested spring (Java) doing something similar but just as an experiment. Having one lambda managing all the API calls seems easier to manage the code but i have never compared it to running each function in a separate lambda (run times). After reading your question I came across these two articles explaining your approach and I would definitely give it a try!

Deploy your ASP.NET Core Web API to AWS Lambda - Written on July 4, 2017 How To Turn Your Existing ASP.net Core API Into A Lambda Function - Written on May 5, 2017

Depending on the game's frequency of accessing the API, recently I went to a meetup about Serverless Use Cases, Challanges and Solutions where three companies (SundaySky, PureSec and Lightricks) described their usage and problems with Api Gateway and Lambdas. The issue of cold start keeps returning and their conclusion is that Java and C# as the language behind lambda results in a longer cold than Python or NodeJS (which have a shorter cold start). Their solution is to pre-warm the containers running the lambdas, and also by defining more memory for the function (memory and CPU scale together).

What are you planning for the Api Gateway to do regarding the Unity game?

Guy

ddough44 commented 7 years ago

@guywald thanks for the reply. First I have to say, a million thank yous for putting this together here on Git. Today I was able to take your example and get the end-to-end connection working on my Lambda from Unity. I'm really surprised at how much work you had to do to make this work. I thought for sure AWS would have built in signing in their SDK. Any idea what's up with that? Is it just a Unity SDK issue or is anyone developing with Cognito having to put this much effort to generate auth headers?

Yeah, for sure, ASP.NET Core is SUPER bad ass. I'm going to try to get your example working with ASP.NET Core once I get some other stuff done. Next on the agenda is getting User creation via User Pools working and Cognito auth to the same APIGateway via the User pools users. Not sure if you tried this yet? I haven't found any examples online yet so I'm just trudging my way through the AWS mobile SDK to figure out how to actually create the users.

I wasn't aware of the cold-start issue, but it makes sense once you bring it up. This is a problem even on traditional IIS hosted web apps. I read some articles online about this after you shared (https://cloudncode.blog/2017/02/13/net-core-web-api-lambda-performance/) and see people have benchmarked it. It seems like warm-up works pretty well, so doesn't seem like a major issue. Worst case, I could create an app that simply 'pings' each of my lambdas every 10 minutes from an admin account.

As far as what I'm planning? I'm working on a massively single player online game. So think lots of socially driven interactions, server authoritative consumable assets, need-to-be-online to play type single player game. So lots of interactions with the server. I plan on creating everything serverless in Lambda via ASP.NET Core/APIGateway, with Dynamo as the DB for most things and MySQL/Aurora for the DB on transactional related services (such as purchasing and consuming purchasable goods). For pushing to the client, I won't need that for the MVP, but when I do, I plan on using PubNub as I'm not creating a real-time game and the 250 MS delay is good enough of a tradeoff to have 'infinite' scalability without needing to create a stateful game server fleet that can be spun up and down. Although I still need to prove it's doable in PubNub, so far it looks pretty solid.

BTW, Unity 2017 made a breaking change to AWS SDK. They changed the WWW class to disallow the changing of certain headers. This means you need to make a tweak to get Cognito to work (by specifying (Amazon.AWSConfigs.HttpClient = Amazon.AWSConfigs.HttpClientOption.UnityWebRequest). Even still after doing this, I couldn't get the WWW class to allow me to inject the auth headers - which means your example doesn't work here as is with 2017. I had to downgrade the project down to the previous version until I have the time to swap out WWW for an aftermarket HTTPClient (or I guess possibly UnityWebRequest).

I had a question for you - how to identify the User uniquely from inside the Lambda? I am right now using request.RequestContext.Identity.CognitoIdentityId... will this work permanently to uniquely identify my user? I plan on using this as the UserID in my databases. Also, is there anything further I need to do inside the Lambda to validate the token? Or does API Gateway automatically manage all that? What about from the client side, if the token expires in an hour, do I need to do some work each hour to manage that?

Sorry for bombing you with quesitions man. Thanks for any help!

guywald commented 7 years ago

Happy to help.

I didn't know about this change. The reason I used WWW is because it let's you add a header. I guess any other HTTP client that allows to do this will do, but when I checked at the time of creating the example, most of them could not change certain headers on mobile. When I will free some time this would certainly be on my To Do list.

Regarding your question, you are correct that Context.Identity.CognitoIdentityId sends you the Cognito id, but to authorize the user in a unique manner they have to use the Authenticated Role, since afaik the unauthenticated could produce different id's to the same user.

If you are using the Cognito User Pools remember to use the uuid and not the username or email. This is mentioned in custom authorizer examples saying that the uuid will never be reasigned. Custom authorizer example - See Understanding the code step 3

The Cognito tokens lifetime, I'd recommend reading: Requesting Temporary Security Credentials

If you are planning to use the providers user pool's access/user token (example Facebook's token to access data on Facebook),

(1) Get the token from the user in the request and then (2) from within the lambda, call the Cognito Identity against that token (e.g. Facebook token) and compare the returned Cognito uuid to the Context.Identity.CognitoIdentityId.

This would prove to me that the token belongs to the calling user - because if I don't compare I can authenticate with Cognito but send someone else's Facebook token. I guess this is a good check but I hadn't got the chance to thoroughly test it.

(3) Once authenticated against Facebook you can keep the token or an MD5 of the token somewhere and each time compare (and avoid calling Cognito from with in the lambda). If the token expires (token is not equal to the one in memory) you can recall the test and save again the new token.

What do you think of this solution?

Guy

ddough44 commented 7 years ago

hi @guywald , thanks for the reply man.

I am still processing everything you wrote and going through some of the links you shared.

One thing I've been banging my head with is that I haven't found much of any examples of how to use User Pools with UnitySDK.

I think I figured out why finally - it seems AWS Unity SDK does not support User Pools. I guess that's an IOS and Android thing but not a Unity thing.

Major Bummer. One of the selling points of AWS is that I could do both Federated Identities from Facebook etc and also custom accounts via User Pools.

I still love everything else about AWS, but something like this makes me re-think my choice and maybe GCP + Firebase is better since Firebase supports email/password based accounts for Unity out of the box. I'm thinking now of trying to get Firebase Auth working with AWS/Lambda. The problem though is Firebase is IOS/Android only and I want to support desktop platforms and eventually game systems.

I think I may still be able to use Custom Authentication / Developer provider authentication using Unity, but it's late and I haven't searched for that yet..... I'm guessing you didn't have the need to allow users to use username/password in your game? Are you just using social logins then?

Thanks!

ddough44 commented 7 years ago

@guywald

Thanks again for your earlier reply. I wanted to respond now that I've processed what your wrote

1) Simply compare the Cognito token against the Cognito API to verify the token is valid. That sounds simple enough - just need to dig into the docs and figure out what method to call and how to do that

2) Caching that token from the Cognito service to DynamoDB so that I don't have to re-call Cognito every time. Just need to make sure that I invalidate the token after expiry time. Seems easy enough!

Thanks again!

BTW - I think I might have a solution for using Cognito with Unity and allowing users to create their own accounts. Instead of using User Pools I'll create a set of Lambdas and build my own auth scheme and then allow this developer authentication to be used in my Unity Game. The good news is an example of doing this already exists here: https://github.com/danilop/LambdAuth/tree/master/www. I just need to make sure they are following best security practices. Hopefully, by the time my game releases AWS will simply support User Pools with the Unity SDK and I won't need to keep this forever

dailenspencer commented 7 years ago

@ddough44 Hey there, few issues I ran into that I thought you might be able to help out on.

I'm having a similar issue with the www header restrictions. Did you find a solution or did you stick with the downgraded version?

Did you come up with a good solution for user authentication through Unity? AWS User Pool Authentication has not yet been released for the Unity SDK - this is what I use for the parallel React.js application i'm creating. Wish I could do the same through the unity app but I guess I need to find the next best solution.

Thanks

guywald commented 7 years ago

Hi @ddough44 and @dailenspencer,

I've fixed and issue with the headers error when running in Unity 2017, tested it and it seems to work fine. (https://github.com/guywald/serverless-auth-msg-board-unity3d-client)

Also for the server side (this repo), I've changed the dynamodb create message methdo to Python (because of problems compiling C# on unix).

Basically the headers error occurred in: 1) Cognito authentication - fixed by changing the default Http library for the Cognito SDK. 2) The headers error in WWW lib, Host is set in the WWW constructor and I removed it from the headers dictionary.

My Unity version is 2017.1

Guy

ddough44 commented 7 years ago

@guywald - thank you SO much for the update. Very cool! @dailenspencer - I wound up ditching AWS and went with GameSparks. I'd strongly recommend checking out GameSparks (recently bought out by AWS, so technically still AWS). If GameSparks fits the rest of your problem space, it may be a home-run to switch like it was for us. They manage Authentication for MANY devices and auth providers - far more than any other cloud offering I've found. Plus, they do all the other high level BAAS stuff.