xamarin.auth with Amazon as a provider

I am using the Xamarin.Auth in Xamarin.Forms using a shared project (not a PCL) and trying to get the amazon presenter to work properly. I believe I have it all down, except the presenter is not closing. I am guessing that it is due to my redirect_uri being incorrect. However, I cannot find what the format that they are sending back. Google wants the client ID returned backwards, others have a URL, etc. When I used it with my Amazon Echo Skill (Audubon Bird Calls) it redirects to some weird login for "layla" so apparently they may not be adhering to any standards....oh yeah, there are not any!

I was hoping to use the Cognito service from Amazon, but ran into similar issues.

Thanks for the guidance!

Best Answers

  • EdwardNorrisEdwardNorris US
    Accepted Answer

    @moljac, you are absolutely right, I am sorry, I should have.

    My solution is below. The main issue was to make sure that you did NOT use the 7 parameter version of the authenticator call but instead use the 5 parameter call for the Non-Native UI. Additionally, the Redirect URI needed to be a valid URL that was white-listed on the Amazon Developer portal for the app.

    Then, just calling the presenter as normal. All non-native, and worked like a charm.

    Ed

    public static string AppName = "Work Amazon!";
    public static string AndroidClientId = "MyClientID";
    public static string Scope = "profile";
    public static string AuthorizeUrl = "https: //www.amazon.com/ap/oa";
    public static string UserInfoUrl = "https: //api.amazon.com/user/profile";
    public static string RedirectUri = "https: //www.myDomain.com";

    public static OAuth2Authenticator Authenticator;
    
    public static void ConfigureAmazonAuthenticator()
    {
        Authenticator = new OAuth2Authenticator(
            AndroidClientId,
            Scope,
            new Uri(AuthorizeUrl),
            new Uri(RedirectUri));
    
        Authenticator.Title = "Linking to Amazon";
        Authenticator.AllowCancel = true;
        Authenticator.Completed += OnAuthCompleted;
        Authenticator.Error += OnAuthError;
    }
    

Answers

  • JohnHardmanJohnHardman GBUniversity mod

    @EdwardNorris - Hopefully the following will help:

    This is the configuration I use for Amazon when using Xamarin.Auth:

        // Details found at https://developer.amazon.com/public/apis/engage/login-with-amazon/docs/obtain_customer_profile.html#call_profile_endpoint
        // and https://developer.amazon.com/public/apis/engage/login-with-amazon/docs/obtain_customer_profile.html
    
        private static Uri ApiEndpoint_Amazon = new Uri("https://api.amazon.com/user/profile?access_token");
        private static readonly Uri AuthorizationEndpoint_Amazon = new Uri("https://www.amazon.com/ap/oa");
        private static readonly Uri RedirectionEndpoint_Amazon = new Uri("https://www.myappdomain.com");
        private static readonly string Scope_Amazon = "profile";
        private static readonly string ServiceId_Amazon = "Amazon";
    

    And this is the code for handling responses from Amazon:

    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Threading.Tasks;
    
    using Newtonsoft.Json.Linq;
    
    using Xamarin.Auth;
    
    using CommonInfrastructure;
    
    namespace myappdomainOAuth
    {
        public class OAuthServerAmazon : IOAuthProfileMethods
        {
            // Details of getting customer profile found at https://developer.amazon.com/public/apis/engage/login-with-amazon/docs/obtain_customer_profile.html
            [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "account")]
            private Task<OAuthProfileInformation> GetProfileInformationFromJsonAsync(
                Account account,
                JObject json)
            {
                OAuthProfileInformation profileInformation = new OAuthProfileInformation();
    
                JObject error = (JObject)json["error"];
                if (error != null)
                {
                    string message = (string)error["message"];
                    string type = (string)error["error"];
                    string request_id = (string)error["request_id"];
    
                    profileInformation.ErrorDetail = string.Format(
                        "Error returned from authorisation server.\r\nmessage = {0}\r\nerror = {1}\r\nrequest_id = {2}",
                        message ?? "{null}",
                        type ?? "{null}",
                        request_id ?? "{null}");
                }
                else
                {
                    // email,user_id,name
    
                    profileInformation.Email = (string)json["email"];
                    profileInformation.FullName = (string)json["name"];
                    profileInformation.Id = (string)json["user_id"];
                }
    
                return Task.FromResult(profileInformation);
            }
    
            public async Task<OAuthProfileInformation> GetProfileInformationAsync(
                IOAuthServerConfiguration serverConfiguration,
                Account account)
            {
                // For Amazon, account contains:
                // [0] = {[access_token, ... ]}
                // [1] = {[token_type, bearer]}
                // [2] = {[expires_in, 3600]}
                // [3] = {[scope, profile]}
                // [4] = {[state, ... ]}
    
                OAuthProfileInformation profileInformation = null;
    
                string requestString = serverConfiguration.ApiEndpoint.ToString();
    
                if (account.Properties.ContainsKey("access_token"))
                {
                    requestString = requestString + account.Properties["access_token"];
    
                    JObject json = await OAuthHelper.MakeRequestAndGetResponseAsJson(
                        account,
                        requestString);
    
                    if (json != null)
                    {
                        profileInformation = await GetProfileInformationFromJsonAsync(account, json);
                    }
                    else
                    {
                        profileInformation = new OAuthProfileInformation { ErrorDetail = "Null json from response message" };
                    }
                }
                else
                {
                    profileInformation = new OAuthProfileInformation { ErrorDetail = "Missing access token" };
                }
    
                if (account.Properties.ContainsKey("expires_in"))
                {
                    string expiresIn = (string)account.Properties["expires_in"];
                    int expiresInMinutes = Convert.ToInt32(expiresIn);
                    DateTime expiryDateTime = DateTime.UtcNow.AddMinutes(expiresInMinutes);
                    profileInformation.TokenExpiryUtc = expiryDateTime;
                }
    
                return profileInformation;
            }
    
        } // public class OAuthServerAmazon : IOAuthProfileMethods
    
    } // namespace myappdomainOAuth
    
    // eof
    
  • Thanks for the response. This is one of the methods that I was attempting to do without using any Native Rendering.
    This opens the Amazon page (with the header of "Authentication"), lets you find the SignIn button, then log in. However, after signing in, it continues to my amazon account, it does not fire off the Complete or Error events. Now, if I hit the back button on my phone, it will go back to my app, fire the Completed event, but the Authorization is always false.

    Would you like me to upload the Native Version that I was also trying?

    It would not let me paste in the code because of the links in it...so I've attached it.

    Thanks everyone!
    Ed

  • JohnHardmanJohnHardman GBUniversity mod

    @EdwardNorris - You don't appear to be setting the ClientSecret as part of the OAuth2Authenticator construction.

    I also do the following:

    • Add a BrowsingCompleted handler to clean up event handlers.

    • On Android

              object obj = authenticator.GetUI(MainActivity.TheInstance);
              Intent intent = (Intent) obj;
              MainActivity.TheInstance.StartActivity(intent);
      
    • On iOS:

              _rootViewController = UIApplication.SharedApplication.KeyWindow.RootViewController;
              object obj = authenticator.GetUI();
              UIViewController uivc = (UIViewController) obj;
              _rootViewController.PresentViewController(uivc, true, null);
      
    • On UWP:

      My code is tucked away in the samples section of the Xamarin.Auth GitHub. A Google should find it.

  • John,
    I appreciate your assistance. Either way, I am still not getting the redirect to happen after the login.
    When I run the code using the presenter, it does actually open the signon in a custom tab and allows the sign-on (i.e. it says "Authentication" at the top). I verified with Fiddler that the call to Amazon is accurate. Once logged in, the redirect is not firing, and it is instead still going through to Amazon's web-site as a logged in user.
    When I changed to native move and used your additional code, the login appears in a standard browser window and has the same affect.
    So, there is something going astray with the callback, or perhaps their presenter does not handle it since Amazon appears to be switching up some of their login methods? I do not know. I am unable to find any working form of using Xamarin.Auth with Amazon from a Shared Project and Android.
    I have everything working fine with Google login, just as a test of my sanity. There are no issues there. But since this app connects to an Echo skill, I really need to have Amazon working properly.
    So, unfortunately, I'm going to have to know when to throw in the towel and try an entirely different approach.
    Again, I do appreciate all that you have provided.

  • Just as FYI, I now have it responding back with "Expected access_token in access token response, but did not receive one."
    One step forward...one baby step...

  • moljacmoljac HRBeta ✭✭✭

    @EdwardNorris

    Didn't you solve your problem. Please post your solution here (w/o sensitive data) so we can all benefit from it.

    thanks

  • EdwardNorrisEdwardNorris USMember
    Accepted Answer

    @moljac, you are absolutely right, I am sorry, I should have.

    My solution is below. The main issue was to make sure that you did NOT use the 7 parameter version of the authenticator call but instead use the 5 parameter call for the Non-Native UI. Additionally, the Redirect URI needed to be a valid URL that was white-listed on the Amazon Developer portal for the app.

    Then, just calling the presenter as normal. All non-native, and worked like a charm.

    Ed

    public static string AppName = "Work Amazon!";
    public static string AndroidClientId = "MyClientID";
    public static string Scope = "profile";
    public static string AuthorizeUrl = "https: //www.amazon.com/ap/oa";
    public static string UserInfoUrl = "https: //api.amazon.com/user/profile";
    public static string RedirectUri = "https: //www.myDomain.com";

    public static OAuth2Authenticator Authenticator;
    
    public static void ConfigureAmazonAuthenticator()
    {
        Authenticator = new OAuth2Authenticator(
            AndroidClientId,
            Scope,
            new Uri(AuthorizeUrl),
            new Uri(RedirectUri));
    
        Authenticator.Title = "Linking to Amazon";
        Authenticator.AllowCancel = true;
        Authenticator.Completed += OnAuthCompleted;
        Authenticator.Error += OnAuthError;
    }
    
Sign In or Register to comment.