Friday, February 21, 2014

AWS Web Identity Federation for Mobile Apps - Amazon (3 of 3 series)

This is last part of a three part series. You may want to read about basic introduction and Facebook authentication and Google authentication before reading this blog. In this blog, I will cover the Amazon authentication in this blog.

Prerequisites

·         Sign up for AWS and get the AccessKey & SecretKey. You can find the info about AWS Account and Access Keys here.
·         Have Visual Studio installed, I used Visual Studio 2013. Although I did not test it, earlier version should work.
·         Install AWS SDK for .Net from here follow the “Getting Started” instructions.

Overview

1.       Register your application and note down the app ID and client ID. Unlike Facebook/Google, you need these two.
2.       Create a role in AWS, this is the role the user will be impersonated.
3.       The app includes logic to make https request to https://www.amazon.com/ap/oa and to get back a token (or code) from the provider.
4.       The app calls AssumeRoleWithWebIdentity without using any AWS security credentials. The call includes the token received from the provider previously.
5.       AWS STS is able to verify that the token passed from the app is valid and then returns temporary security credentials to the app. The mobile app's permissions to access AWS are established by the role that the app assumes.

Register Application

Create an app at App Console. Save the client ID and client secret in the app.config as described below. You should also define the redirect URL, only https is supported. I chose to use https://google.com. This is the only redirect URL allowed to receive the token.


Create an AWS Role

This is the role that an Amazon authenticated user will assume. The role is associated with two things a) trust policy – who can assume this role and b) access policy – what permission does the assumed user have.

C# code below creates a role. Normally, this is manually created once. I chose to write C# code because it is handy for automation. This role can be assumed by any authenticated Amazon user. The user only has access to their specific key which is located under “federationbucket/Amazon/”. Code below is slightly complicated because the same code works for all the identity providers (i.e.) Facebook/Google/Amazon.

    providerURL = "www.amazon.com";
    providerAppIdName = "app_id";
    providerUserIdName = "user_id";

    //identity provider specific AppId is loaded from app.config (e.g)
    //  FacebookProviderAppId. GoogleProviderAppId, AmazonProviderAppId
    providerAppId = ConfigurationManager.AppSettings[identityProvider +
                                                        "ProviderAppId"];

    // Since the string is passed to String.Format, '{' & '}' has to be escaped.
    // Policy document specifies who can invoke AssumeRoleWithWebIdentity
    string trustPolicyTemplate = @"{{
            ""Version"": ""2012-10-17"",
            ""Statement"": [
                {{
                        ""Effect"": ""Allow"",
                        ""Principal"": {{ ""Federated"": ""{1}"" }},
                        ""Action"": ""sts:AssumeRoleWithWebIdentity"",
                        ""Condition"": {{
                            ""StringEquals"": {{""{1}:{2}"": ""{3}""}}
                        }}
                }}
            ]
        }}";

    // Defines what permissions to grant when AssumeRoleWithWebIdentity is called
    string accessPolicyTemplate = @"{{
            ""Version"": ""2012-10-17"",
            ""Statement"": [
            {{
                ""Effect"":""Allow"",
                ""Action"":[""s3:GetObject"", ""s3:PutObject"", ""s3:DeleteObject""],
                ""Resource"": [
                        ""arn:aws:s3:::federationtestbucket/{0}/${{{1}:{4}}}"",
                        ""arn:aws:s3:::federationtestbucket/{0}/${{{1}:{4}}}/*""
                ]
            }}
            ]
        }}";

    // Create Trust policy
    CreateRoleRequest createRoleRequest = new CreateRoleRequest
    {
        RoleName = "federationtestrole",
        AssumeRolePolicyDocument = string.Format(trustPolicyTemplate,
                                                    identityProvider,
                                                    providerURL,
                                                    providerAppIdName,
                                                    providerAppId)
    };
    Console.WriteLine("\nTrust Policy Document:\n{0}\n",
        createRoleRequest.AssumeRolePolicyDocument);
    CreateRoleResponse createRoleResponse = iamClient.CreateRole(createRoleRequest);

    // Create Access policy (Permissions)
    PutRolePolicyRequest putRolePolicyRequest = new PutRolePolicyRequest
    {
        PolicyName = "federationtestrole-rolepolicy",
        RoleName = "federationtestrole",
        PolicyDocument = string.Format(accessPolicyTemplate,
                                        identityProvider,
                                        providerURL,
                                        providerAppIdName,
                                        providerAppId,
                                        providerUserIdName)

    };
    Console.WriteLine("\nAccess Policy Document (Permissions):\n{0}\n",
                                        putRolePolicyRequest.PolicyDocument);
    PutRolePolicyResponse putRolePolicyResponse = iamClient.PutRolePolicy(
                                                        putRolePolicyRequest);

Above code assumes an app.config file to contain the following values.
    <appSettings>
        <add key="AWSAccessKey" value="YOUR_ACCESS_KEY_A134" />
        <add key="AWSSecretKey" value="YOUR_SECRET_KEY_HERE_SECRET_KEY_HEREndgN" />
        <add key="AWSRegion" value="us-east-1" />
        <add key="AmazonProviderAppId"
             value="amzn1.application.your_app_id_here_your_appid_here" />
        <add key="AmazonProviderClientId"
             value="amzn1.application-oa2-client.your_client_id_here_client_id_ab" />
    </appSettings>

Trust Policy document produced by the above code:
{
  "Version": "2012-10-17",
  "Statement": [
     {
       "Effect": "Allow",
       "Principal": { "Federated": "www.amazon.com" },
       "Action": "sts:AssumeRoleWithWebIdentity",
       "Condition": {
         "StringEquals": {"www.amazon.com:app_id":
                     "amzn1.application-oa2-client.your_client_id_here_client_id_ab"}
          }
      }
    ]
}

Access Policy document (permissions) produced by the above code:
{
    "Version": "2012-10-17",
    "Statement": [
       {
         "Effect":"Allow",
         "Action":["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
         "Resource": [
           "arn:aws:s3:::federationtestbucket/Amazon/${www.amazon.com:user_id}",
           "arn:aws:s3:::federationtestbucket/Amazon/${www.amazon.com:user_id}/*"
          ]
       }
    ]
}

Authenticate with Amazon and get the token

The authorization sequence begins when your application redirects a browser to Amazon URL (https://www.amazon.com/ap/oa); the URL includes query parameters that indicate the type of access being requested. Amazon handles user authentication, session selection, and user consent. The result is an authorization code, which Amazon returns to your application in a query string.

The code below constructs the query and makes the http call to retrieve the token directly. The GET action is performed in the browser control. If the authentication succeeds, it will be redirected to the URL specified in the query (This redirect URL has to be pre-configured as described above). The C# code below uses the Forms based WebBrowser control to automate this process. As soon as the token is retrieved, the browser control is closed. The structure of the query is pretty straight forward. It can be inferred by looking at the code below.

    string query = "https://www.amazon.com/ap/oa?" +
                    string.Format("client_id={0}&", client_id) +
                    "response_type=token&" +
                    "scope=profile&" +
                    "redirect_uri=https://www.google.com";

The GetToken helper function, does GET operation and retrieves the token from the redirected URL.
    class MyWebBrowser : WebBrowser
    {
        public string CapturedUrl;
        string token;
        public MyWebBrowser(string token)
        {
            this.token = token + "=";
        }

        protected override void OnDocumentCompleted(
            WebBrowserDocumentCompletedEventArgs e)
        {
            base.OnDocumentCompleted(e);
            string st = e.Url.ToString();
            if (st.Contains(token))
            {
                // hack, closing the form here does not work always.
                this.Navigate("about:blank");
                this.CapturedUrl = st;
                Console.WriteLine("Captured: {0}", st);
            }
            else if (st == "about:blank")
            {
                ((Form)this.Parent).Close();
            }
        }
    }

    string GetToken(string token, string url)
    {
        Form f = new Form();
        MyWebBrowser wb = new MyWebBrowser(token);
        wb.Dock = DockStyle.Fill;
        f.Controls.Add(wb);
        wb.Navigate(url);
        f.WindowState = FormWindowState.Maximized;
        f.ShowDialog();

        string st = wb.CapturedUrl;
        f.Dispose();

        if (st == null)
            throw new Exception("Oops! Error getting the token");

        int index = st.IndexOfAny(new char[] { '?', '#' });
        st = index < 0 ? "" : st.Substring(index + 1);
        NameValueCollection pairs = HttpUtility.ParseQueryString(st);

        string tokenValue = pairs[token];
        Console.WriteLine("TOKEN={0}, Value={1}", token, tokenValue);
        return tokenValue;
    }

Get Temporary Credentials with AssumeRoleWithWebIdentity

Key concept to grasp here is, you start with anonymous AWS credentials, pass the token received from Amazon and get the temporary credentials. This is important because the mobile app user will not have any AWS credentials.
    public AssumeRoleWithWebIdentityResponse GetTemporaryCredentialUsingAmazon(
        string client_id,
        string role)
    {
        string query = "https://www.amazon.com/ap/oa?" +
                        string.Format("client_id={0}&", client_id) +
                        "response_type=token&" +
                        "scope=profile&" +
                        "redirect_uri=https://www.google.com";

        AssumeRoleWithWebIdentityRequest assumeRoleWithWebIdentityRequest =
            new AssumeRoleWithWebIdentityRequest ()
            {
                ProviderId = "www.amazon.com",
                WebIdentityToken = GetToken("access_token", query),
                RoleArn = role
            };
        return GetAssumeRoleWithWebIdentityResponse(
            assumeRoleWithWebIdentityRequest);
    }

References


You can find the code under “AWS\AWS CSharp Test” folder at https://github.com/padisetty/Samples.

Explore & Enjoy!

/Siva

28 comments:

  1. Thank you for your comment, feel free to suggest useful topics.

    ReplyDelete
  2. nice piece of information, I had come to know about your internet site from my friend vinay, delhi,i have read atleast 12 posts of yours by now, and let me tell you, your website gives the best and the most interesting information. This is just the kind of information that i had been looking for, i'm already your rss reader now and i would regularly watch out for the new post, once again hats off to you! Thanx a ton once again, Regards, obiee training in hyderabad

    ReplyDelete
  3. If you develop any app then its very important to test it, whether it is working properly or not, the user interface, the icons, the layout everything is needed to be tested any if there is any bug issues then it has to be resolved at the earliest. Testing an app is as important as developing an app.
    mobile application testing

    ReplyDelete
  4. Useful post.Mobile application testing is a procedure by which application programming produced for handheld cell phones is tested for its consistency, functionality and usability.
    Thanks,
    Mobile Testing Training in Chennai | Mobile Testing Training | Mobile Apps Testing

    ReplyDelete
  5. Mobile apps can help increase brand awareness and also make people more loyal. It's simple, when your company's name or logo is on the user's mobile screen, they will find it difficult to forget or neglect it. Make money

    ReplyDelete
  6. Compose a digital book, enroll for a free Amazon.com account, visit the computerized content stage, enter in your item depiction, undercover your digital book to advanced content, set your offering cost, and distribute.Click here

    ReplyDelete
  7. Code below creates a role. Normally, this is manually created once.Web design

    ReplyDelete
  8. So coming off several posts on DevOps and how its practices combined with agile development can lead to an improved execution, I thought to share some incites on how to recognize and improve IT culture.devops openings in hyderabad

    ReplyDelete
  9. Hi, I have read your blog. Really very informative and excellent post I had ever seen about AWS. Thank you for sharing such a wonderful blog to our vision. Learn AWS Training in Chennai to know more details about this technology.
    Web Designing Training in Chennai | Dot Net Training in Chennai

    ReplyDelete
  10. Hello admin, I have read your blog, it was very nice to read & I am getting useful information’s through your blog. Keep update your blog. AWS Training in Bangalore | Big Data Hadoop Training in Bangalore

    ReplyDelete
  11. This is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.
    Aws Online Training

    ReplyDelete
  12. Superb. I really enjoyed very much with this article here. Really it is an amazing article I had ever read. I hope it will help a lot for all. Thank you so much for this amazing posts and please keep update like this excellent article.thank you for sharing such a great blog with us.
    AWS Training in Chennai | Best AWS Training in Chennai

    ReplyDelete
  13. The creator has so delightfully enchanted the thought of group by this brilliant blog.Web design in Bellevue NE

    ReplyDelete
  14. Utilization of refined and mesmeric is all that is expected to deliver such a grand blog.
    phone tracker

    ReplyDelete
  15. Established in 2013 to provide exciting, effective design solutions. Since its inception, Globalwebsolution has grown considerably into a recognised brand design and digital marketing innovator. Rewarding our clients with compelling visual solutions that create value and recognition in their marketplace.

    ReplyDelete
  16. The creator has so delightfully enchanted the thought of group by this brilliant blog
    AWS Jobs in Hyderabad .

    ReplyDelete