patriczi
patriczi

Reputation: 51

API with Xamarin.Essentials: Web Authenticator apple error 500

On my API server I use Xamarin.Essentials: Web Authenticator to login via social account (google, fb, apple). It works fine for Google and Facebook logins, but not Apple. Nginx when logging in via Google returns something like this:

 - [15/Mar/2022:07:10:57 +0000] "GET /api/mobileauth/Google HTTP/2.0" 302 0 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1"
 - [15/Mar/2022:07:10:58 +0000] "GET /signin-google?state=CfD..(e.t.c)

I can handle it, and it works fine, but when I try to log in, Apple returns error 500 like this:

 - [15/Mar/2022:07:14:11 +0000] "GET /api/mobileauth/Apple HTTP/2.0" 302 0 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1"
 - [15/Mar/2022:07:14:34 +0000] "POST /signin-apple HTTP/2.0" 500 0 "https://appleid.apple.com/" "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1"

Maybe someone had a similar problem and knows why is this happening? The server goes through Cloudflare which has full SSL. Anyone have any ideas? my startup:

services.AddAuthentication(o =>
            {
                o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
                .AddFacebook(fb =>
                {
                    fb.AppId = "my AppId";
                    fb.AppSecret = "my AppSecret ";
                    fb.SaveTokens = true;
                })
                 .AddGoogle(g =>
                 {
                     g.ClientId = "my ClientId ";
                     g.ClientSecret = "my ClientSecret ";
                     g.SaveTokens = true;
                 })
                 .AddApple(a =>
                 {
                     a.ClientId = "my ClientId";
                     a.KeyId = "my KeyId ";
                     a.TeamId = "my TeamId ";
                     a.UsePrivateKey(keyId
                         => WebHostEnvironment.ContentRootFileProvider.GetFileInfo($"AuthKey_my_KeyId.p8"));
                     a.SaveTokens = true;
                 });

my controller:

 [HttpGet("{scheme}")]
        public async Task Get([FromRoute] string scheme)
        {
            try
            {
                var auth = await Request.HttpContext.AuthenticateAsync(scheme);
                if (!auth.Succeeded
                    || auth?.Principal == null
                    || !auth.Principal.Identities.Any(id => id.IsAuthenticated)
                    || string.IsNullOrEmpty(auth.Properties.GetTokenValue("access_token")))
                {
                    // Not authenticated, challenge
                    await Request.HttpContext.ChallengeAsync(scheme);
                }
                else
                {
                    var claims = auth.Principal.Identities.FirstOrDefault()?.Claims;
                    var email = string.Empty;
                    email = claims?.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Email)?.Value;

                    // Get parameters to send back to the callback
                    var qs = new Dictionary<string, string>
                {
                    { "access_token", auth.Properties.GetTokenValue("access_token") },
                    { "refresh_token", auth.Properties.GetTokenValue("refresh_token") ?? string.Empty },
                    { "expires", (auth.Properties.ExpiresUtc?.ToUnixTimeSeconds() ?? -1).ToString() },
                    { "email", email }
                };

                    // Build the result url
                    var url = callbackScheme + "://#" + string.Join(
                        "&",
                        qs.Where(kvp => !string.IsNullOrEmpty(kvp.Value) && kvp.Value != "-1")
                        .Select(kvp => $"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}"));
                    // Redirect to final url
                    Request.HttpContext.Response.Redirect(url);
                }
            }
            catch(Exception ex)
            {
                _logger.Error("Exception!" + ex + " Model:" + scheme);
            }
        }

Upvotes: 1

Views: 298

Answers (1)

Amjad S.
Amjad S.

Reputation: 1241

First, configure your app to use Apple Sign In. https://learn.microsoft.com/en-us/xamarin/ios/platform/ios13/sign-in

For iOS 13 and higher, use the AppleSignInAuthenticator.AuthenticateAsync() method. This will call the native Apple Sign in API’s under the hood so users get the best experience possible on these devices. Write your shared code to use the right API at runtime like this:

 var scheme = "..."; // Apple, Microsoft, Google, Facebook, etc.
    WebAuthenticatorResult r = null;
    
    if (scheme.Equals("Apple")
        && DeviceInfo.Platform == DevicePlatform.iOS
        && DeviceInfo.Version.Major >= 13)
    {
        // Use Native Apple Sign In API's
        r = await AppleSignInAuthenticator.AuthenticateAsync();
    }
    else
    {
        // Web Authentication flow
        var authUrl = new Uri(authenticationUrl + scheme);
        var callbackUrl = new Uri("xamarinessentials://");
    
        r = await WebAuthenticator.AuthenticateAsync(authUrl, callbackUrl);
    }
    
    var accessToken = r?.AccessToken;

Check full article

Upvotes: 0

Related Questions