Example #1
0
        public static AppleAccount FromUrl(string url)
        {
            var parameters = Util.ParseUrlParameters(url);

            var idToken = JwtToken.Decode(parameters["id_token"]);

            return(new AppleAccount
            {
                IdToken = idToken,
                UserId = idToken.Subject,
                Email = idToken.Payload.ContainsKey("email") ? idToken.Payload["email"]?.ToString() : null,
                Name = idToken.Payload.ContainsKey("name") ? idToken.Payload["name"]?.ToString() : null,
                AccessToken = parameters.ContainsKey("access_token") ? parameters["access_token"] : null,
                RefreshToken = parameters.ContainsKey("refresh_token") ? parameters["refresh_token"] : null,
            });
        }
Example #2
0
        public async Task <AppleAccount> ExchangeTokenAsync(string code)
        {
            var client          = new HttpClient();
            var header          = new ProductHeaderValue("Xamarin", "1.0");
            var userAgentHeader = new ProductInfoHeaderValue(header);

            client.DefaultRequestHeaders.UserAgent.Add(userAgentHeader);

            var secret = GenerateClientSecretJWT(P8FileContents);

            var resp = await client.PostAsync(AppleTokenUrl, new FormUrlEncodedContent(new Dictionary <string, string>
            {
                { "grant_type", "authorization_code" },
                { "code", code },
                { "redirect_uri", RedirectUri.OriginalString },
                { "client_id", ServerId },
                { "client_secret", secret },
            }));

            resp.EnsureSuccessStatusCode();

            var respData = await resp.Content.ReadAsStringAsync();

            var respProperties = Newtonsoft.Json.JsonConvert.DeserializeObject <Dictionary <string, string> >(respData);

            var idToken      = JwtToken.Decode(respProperties["id_token"]);
            var accessToken  = respProperties?["access_token"];
            var refreshToken = respProperties?["refresh_token"];

            string email = idToken.Payload.ContainsKey("email") ? idToken.Payload["email"]?.ToString() : null;
            string name  = idToken.Payload.ContainsKey("name") ? idToken.Payload["name"]?.ToString() : null;

            if (name == null)
            {
                try
                {
                    JObject obj = JObject.Parse(respData);
                    name = obj["user"]["name"]["firstName"].ToString() ?? null;
                }
                catch (Exception ex)
                {
                    name = null;
                }
            }

            // Validate id token
            if (!idToken.Issuer.Equals(AppleJwtUrl))
            {
                throw new ProtocolViolationException($"Invalid id_token issuer received: Expected `{AppleJwtUrl}` but observed `{idToken.Issuer}`");
            }

            if (!idToken.AccessTokenHash.Equals(Util.Sha256AtHash(accessToken)))
            {
                throw new ProtocolViolationException("Access Token Hash did not match actual access token's hash");
            }

            if (!idToken.Algorithm.Equals("RS256"))
            {
                throw new ProtocolViolationException($"Invalid id_token algorithm returned: Expected `RS256` but observed `{idToken.Algorithm}`");
            }

            // Verify audience claim
            if (!idToken.Audience.Equals(ServerId))
            {
                throw new ProtocolViolationException($"The id_token 'aud' claim does not match the provided clientId value.")
                ;
            }
            if (idToken.Expiration < DateTime.UtcNow)
            {
                throw new ProtocolViolationException($"The id_token is expired");
            }

            // TODO: Verify the signature of the JWT token

            return(new AppleAccount
            {
                Email = email,
                Name = name,
                AccessToken = accessToken,
                RefreshToken = refreshToken,
                IdToken = idToken
            });
        }