Example #1
0
        protected override async Task <AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
        {
            JObject user = await this.RequestWeiboUidAsync(tokens);


            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, WeiboHelper.GetId(user), ClaimValueTypes.String));

            OAuthCreatingTicketContext context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity),
                                                                                properties, this.Context, this.Scheme, this.Options, this.Backchannel, tokens, user);

            await this.Options.Events.CreatingTicket(context);

            context.RunClaimActions();

            await Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
Example #2
0
        protected override async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
        {
            var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            var response = await Backchannel.SendAsync(request, Context.RequestAborted);
            if (!response.IsSuccessStatusCode)
                throw new HttpRequestException($"Failed to retrieve Discord user information ({response.StatusCode}).");

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);
            context.RunClaimActions();

            await Events.CreatingTicket(context);
            return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
        }
Example #3
0
    protected override async Task <AuthenticationTicket> CreateTicketAsync(
        [NotNull] ClaimsIdentity identity,
        [NotNull] AuthenticationProperties properties,
        [NotNull] OAuthTokenResponse tokens)
    {
        // Note: MYOB doesn't provide a user information endpoint,
        // so we rely on the details sent back in the token request.
        var user = tokens.Response !.RootElement.GetProperty("user");

        var principal = new ClaimsPrincipal(identity);
        var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, user);

        context.RunClaimActions();

        await Events.CreatingTicket(context);

        return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
    }
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity, [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary <string, string>
            {
                ["access_token"] = tokens.AccessToken
            });

            var response = await Backchannel.GetAsync(address);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving user information.");
            }

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            var status = payload.Value <long>("userid");

            if (status <= 0)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving user information.");
            }

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions(payload);

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
Example #5
0
        protected override async Task <AuthenticationTicket> CreateTicketAsync(
            [NotNull] ClaimsIdentity identity,
            [NotNull] AuthenticationProperties properties,
            [NotNull] OAuthTokenResponse tokens)
        {
            string accessSecret = GetMD5Hash(tokens.AccessToken + Options.ClientSecret);
            string sign         = GetMD5Hash($"application_key={Options.PublicSecret}format=jsonmethod=users.getCurrentUser{accessSecret}");

            string address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary <string, string?>
            {
                ["application_key"] = Options.PublicSecret ?? string.Empty,
                ["format"]          = "json",
                ["method"]          = "users.getCurrentUser",
                ["sig"]             = sign,
                ["access_token"]    = tokens.AccessToken,
            });

            using var request = new HttpRequestMessage(HttpMethod.Get, address);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync(Context.RequestAborted));

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

            context.RunClaimActions();

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
                                                                               [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary <string, string>
            {
                ["access_token"] = tokens.AccessToken,
                ["v"]            = !string.IsNullOrEmpty(Options.ApiVersion) ? Options.ApiVersion : VkontakteAuthenticationDefaults.ApiVersion
            });

            if (Options.Fields.Count != 0)
            {
                address = QueryHelpers.AddQueryString(address, "fields", string.Join(",", Options.Fields));
            }

            var response = await Backchannel.GetAsync(address, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            var container = JObject.Parse(await response.Content.ReadAsStringAsync());
            var payload   = container["response"].First as JObject;

            if (tokens.Response["email"] != null)
            {
                payload.Add("email", tokens.Response["email"]);
            }

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions(payload);

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
                                                                               [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            var queryArguments = new Dictionary <string, string>
            {
                ["access_token"] = tokens.AccessToken,
                ["site"]         = Options.Site,
            };

            if (!string.IsNullOrEmpty(Options.RequestKey))
            {
                queryArguments["key"] = Options.RequestKey;
            }

            var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, queryArguments);

            var request = new HttpRequestMessage(HttpMethod.Get, address);

            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions(payload.Value <JArray>("items")?[0] as JObject);

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
                                                                               [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            // Note: unlike the other social providers, the userinfo endpoint is user-specific and can't be set globally.
            // For more information, see https://developer.salesforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com
            HttpRequestMessage  request  = null;
            HttpResponseMessage response = null;

            try
            {
                request = new HttpRequestMessage(HttpMethod.Get, tokens.Response.Value <string>("id"));

                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
                request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

                if (!response.IsSuccessStatusCode)
                {
                    Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                    "returned a {Status} response with the following payload: {Headers} {Body}.",
                                    /* Status: */ response.StatusCode,
                                    /* Headers: */ response.Headers.ToString(),
                                    /* Body: */ await response.Content.ReadAsStringAsync());

                    throw new HttpRequestException("An error occurred while retrieving the user from the Salesforce identity service.");
                }

                var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

                var principal = new ClaimsPrincipal(identity);
                var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);
                context.RunClaimActions(payload);

                await Options.Events.CreatingTicket(context);

                return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
            }
            finally
            {
                request?.Dispose();
                response?.Dispose();
            }
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
                                                                               [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);

            using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync());

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

            context.RunClaimActions();

            // When the email address is not public, retrieve it from
            // the emails endpoint if the user:email scope is specified.
            if (!string.IsNullOrEmpty(Options.UserEmailsEndpoint) &&
                !identity.HasClaim(claim => claim.Type == ClaimTypes.Email) && Options.Scope.Contains("user:email"))
            {
                var address = await GetEmailAsync(tokens);

                if (!string.IsNullOrEmpty(address))
                {
                    identity.AddClaim(new Claim(ClaimTypes.Email, address, ClaimValueTypes.String, Options.ClaimsIssuer));
                }
            }

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
Example #10
0
        /// <summary>
        /// 创建身份票据(这是第三步)
        /// </summary>
        /// <param name="identity"></param>
        /// <param name="properties"></param>
        /// <param name="tokens"></param>
        /// <returns></returns>
        protected override async Task <AuthenticationTicket> CreateTicketAsync(
            ClaimsIdentity identity,
            AuthenticationProperties properties,
            OAuthTokenResponse tokens)
        {
            var openId  = GetOpenId(tokens.Response);
            var unionId = GetUnionId(tokens.Response);
            // 构造用户信息JsonElement
            var payloadDic = new Dictionary <string, object>()
            {
                ["openid"] = openId
            };
            var userInfo = JsonDocument.Parse(JsonSerializer.Serialize(payloadDic));

            //微信获取用户信息是需要开通权限的,没有开通权限的只能用openId来标示用户
            if (!string.IsNullOrEmpty(unionId))
            {
                //获取用户信息
                var parameters = new Dictionary <string, string>
                {
                    { "openid", openId },
                    { "access_token", tokens.AccessToken },
                    { "lang", "zh-CN" } //如果是多语言,这个参数该怎么获取?
                };
                var userInfoEndpoint = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, parameters);
                var userInfoResponse = await Backchannel.GetAsync(userInfoEndpoint, Context.RequestAborted);

                if (!userInfoResponse.IsSuccessStatusCode)
                {
                    throw new HttpRequestException(
                              $"未能获取到微信用户个人信息(返回状态码:{userInfoResponse.StatusCode}),请检查access_token是正确。");
                }

                userInfo = JsonDocument.Parse(await userInfoResponse.Content.ReadAsStringAsync());
            }

            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme,
                                                         Options, Backchannel, tokens, userInfo.RootElement);

            context.RunClaimActions();
            await Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, Microsoft.AspNetCore.Authentication.AuthenticationProperties properties, OAuthTokenResponse tokens)
        {
            var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, "access_token", tokens.AccessToken);

            if (Options.Fields.Count != 0)
            {
                address = QueryHelpers.AddQueryString(address, "fields", string.Join(",", Options.Fields));
            }

            var response = await Backchannel.GetAsync(address, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
            var user    = (JObject)payload["response"][0];

            foreach (var scope in Options.Scope)
            {
                var scope_value = tokens.Response.Value <string>(scope);
                if (!string.IsNullOrEmpty(scope_value))
                {
                    user.Add(scope, scope_value);
                }
            }


            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, user);

            context.RunClaimActions();


            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
        {
            var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, "access_token", tokens.AccessToken);

            address = QueryHelpers.AddQueryString(address, "v", Options.ApiVersion);

            if (Options.Fields.Count != 0)
            {
                address = QueryHelpers.AddQueryString(address, "fields", string.Join(",", Options.Fields));
            }

            var response = await Backchannel.GetAsync(address, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);
            var user    = (JObject)payload["response"][0];

            identity.AddOptionalClaim(ClaimTypes.NameIdentifier, VkontakteHelper.GetId(user), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.Name, VkontakteHelper.GetName(user), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.GivenName, VkontakteHelper.GetFirstName(user), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.Surname, VkontakteHelper.GetLastName(user), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.Hash, VkontakteHelper.GetHash(user), Options.ClaimsIssuer)
            .AddOptionalClaim("urn:vkontakte:photo:link", VkontakteHelper.GetPhoto(user), Options.ClaimsIssuer)
            .AddOptionalClaim("urn:vkontakte:photo_thumb:link", VkontakteHelper.GetPhotoThumbnail(user), Options.ClaimsIssuer);

            context.RunClaimActions();

            await Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
Example #13
0
        protected override async Task <AuthenticationTicket> CreateTicketAsync(
            [NotNull] ClaimsIdentity identity,
            [NotNull] AuthenticationProperties properties,
            [NotNull] OAuthTokenResponse tokens)
        {
            string address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, "access_token", tokens.AccessToken);

            if (Options.UseSignedRequests)
            {
                // Compute the HMAC256 signature.
                string signature = ComputeSignature(address);

                // Add the signature to the query string.
                address = QueryHelpers.AddQueryString(address, "sig", signature);
            }

            using var request = new HttpRequestMessage(HttpMethod.Get, address);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync());

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

            context.RunClaimActions(payload.RootElement.GetProperty("data"));

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
Example #14
0
    protected override async Task <AuthenticationTicket> CreateTicketAsync(
        [NotNull] ClaimsIdentity identity,
        [NotNull] AuthenticationProperties properties,
        [NotNull] OAuthTokenResponse tokens)
    {
        var parameters = new Dictionary <string, string?>
        {
            ["access_token"] = tokens.AccessToken,
            ["v"]            = !string.IsNullOrEmpty(Options.ApiVersion) ? Options.ApiVersion : VkontakteAuthenticationDefaults.ApiVersion,
        };

        var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, parameters);

        if (Options.Fields.Count != 0)
        {
            address = QueryHelpers.AddQueryString(address, "fields", string.Join(',', Options.Fields));
        }

        using var response = await Backchannel.GetAsync(address, Context.RequestAborted);

        if (!response.IsSuccessStatusCode)
        {
            await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);

            throw new HttpRequestException("An error occurred while retrieving the user profile.");
        }

        using var container  = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));
        using var enumerator = container.RootElement.GetProperty("response").EnumerateArray();
        var payload = enumerator.First();

        var principal = new ClaimsPrincipal(identity);
        var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);

        context.RunClaimActions();

        // Re-run to get the email claim from the tokens response
        context.RunClaimActions(tokens.Response !.RootElement);

        await Events.CreatingTicket(context);

        return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
    }
Example #15
0
    protected override async Task <AuthenticationTicket> CreateTicketAsync(
        [NotNull] ClaimsIdentity identity,
        [NotNull] AuthenticationProperties properties,
        [NotNull] OAuthTokenResponse tokens)
    {
        using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);

        using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

        if (!response.IsSuccessStatusCode)
        {
            await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);

            throw new HttpRequestException("An error occurred while retrieving the user profile.");
        }

        using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));

        var principal = new ClaimsPrincipal(identity);
        var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

        var doesObjectsExist = payload.RootElement.TryGetProperty("objects", out var accounts);

        if (!doesObjectsExist)
        {
            throw new InvalidOperationException("Property objects is missing on accounts JSON.");
        }

        var hasAtLeastOneAccount = accounts.GetArrayLength() > 0;

        if (!hasAtLeastOneAccount)
        {
            throw new InvalidOperationException("Accounts JSON does not contains any account.");
        }

        context.RunClaimActions(accounts[0]);

        await Events.CreatingTicket(context);

        return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
    }
    protected override async Task <AuthenticationTicket> CreateTicketAsync(
        [NotNull] ClaimsIdentity identity,
        [NotNull] AuthenticationProperties properties,
        [NotNull] OAuthTokenResponse tokens)
    {
        var parameters = new Dictionary <string, string?>
        {
            ["access_token"] = tokens.AccessToken,
            ["openid"]       = tokens.Response?.RootElement.GetString("openid"),
        };

        var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, parameters);

        using var response = await Backchannel.GetAsync(address);

        if (!response.IsSuccessStatusCode)
        {
            await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);

            throw new HttpRequestException("An error occurred while retrieving user information.");
        }

        var json = await response.Content.ReadAsStringAsync(Context.RequestAborted);

        using var payload = JsonDocument.Parse(json);

        var errorCode = payload.RootElement.GetString("errcode");

        if (!string.IsNullOrEmpty(errorCode))
        {
            Log.UserProfileErrorCode(Logger, errorCode, response.Headers.ToString(), json);
            throw new HttpRequestException("An error occurred while retrieving user information.");
        }

        var principal = new ClaimsPrincipal(identity);
        var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

        context.RunClaimActions();

        await Events.CreatingTicket(context);

        return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
    }
Example #17
0
        protected override async Task <HandleRequestResult> HandleRemoteAuthenticateAsync()
        {
            var protectedText = Request.Query["state"].ToArray().FirstOrDefault();
            var properties    = Options.StateDataFormat.Unprotect(protectedText);

            // simulator token return from oauth server
            var tokenObject = Options.PassthroughTokenHandler?.Invoke() ?? new
            {
                token_type    = "bearer",
                access_token  = "1234567890",
                refresh_token = "0987654321",
                expires_in    = "3600",
                user_id       = "nepton_h23f620G2Xw2812aM3a9Z",
                screen_name   = "nepton",
            };

            var token = OAuthTokenResponse.Success(JsonDocument.Parse(JsonSerializer.Serialize(tokenObject)));

            if (token.Error != null)
            {
                return(HandleRequestResult.Fail(token.Error, properties));
            }

            var identity = new ClaimsIdentity(ClaimsIssuer);
            var context  = new OAuthCreatingTicketContext(
                new ClaimsPrincipal(identity),
                properties,
                Context,
                Scheme,
                Options,
                Backchannel,
                token,
                token.Response.RootElement);

            context.RunClaimActions();

            await OAuthEvents.CreatingTicket(context);

            var ticket = new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);

            return(HandleRequestResult.Success(ticket));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
                                                                               [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            // Note: the ArcGIS API doesn't support content negotiation via headers.
            var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary <string, string>
            {
                ["f"]     = "json",
                ["token"] = tokens.AccessToken
            });

            var request = new HttpRequestMessage(HttpMethod.Get, address);

            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            // Request the token
            var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            // Note: error responses always return 200 status codes.
            var error = payload.Value <JObject>("error");

            if (error != null)
            {
                // See https://developers.arcgis.com/authentication/server-based-user-logins/ for more information
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a response with the following error code: {Code} {Message}.",
                                /* Code: */ error.Value <string>("code"),
                                /* Message: */ error.Value <string>("message"));

                throw new InvalidOperationException("An error occurred while retrieving the user profile.");
            }

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions(payload);

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
Example #19
0
        protected override async Task <AuthenticationTicket> CreateTicketAsync(
            ClaimsIdentity identity,
            AuthenticationProperties properties,
            OAuthTokenResponse tokens)
        {
            // Get the Twitch user
            using (var request = new HttpRequestMessage(
                       HttpMethod.Get,
                       Options.UserInformationEndpoint))
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("OAuth", tokens.AccessToken);

                var response = await Backchannel.SendAsync(request, Context.RequestAborted);

                if (!response.IsSuccessStatusCode)
                {
                    throw new HttpRequestException(
                              $"An error occurred when retrieving user information ({response.StatusCode})." +
                              $" Please check if the authentication information is correct " +
                              $"and the corresponding Twitch API is enabled.");
                }

                var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

                var principal = new ClaimsPrincipal(identity);
                var ticket    = new AuthenticationTicket(principal, properties, Scheme.Name);
                var context   = new OAuthCreatingTicketContext(
                    principal,
                    properties,
                    Context,
                    Scheme,
                    Options,
                    Backchannel,
                    tokens,
                    payload);
                context.RunClaimActions();

                await Events.CreatingTicket(context);

                return(context.Result.Ticket);
            }
        }
Example #20
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="identity"></param>
        /// <param name="properties"></param>
        /// <param name="tokens"></param>
        /// <returns></returns>
        protected override async Task <AuthenticationTicket> CreateTicketAsync(
            ClaimsIdentity identity,
            AuthenticationProperties properties,
            OAuthTokenResponse tokens)
        {
            //获取OpenId
            var openIdEndpoint = QueryHelpers.AddQueryString(Options.OpenIdEndpoint, "access_token", tokens.AccessToken);
            var openIdResponse = await Backchannel.GetAsync(openIdEndpoint, Context.RequestAborted);

            if (!openIdResponse.IsSuccessStatusCode)
            {
                throw new HttpRequestException($"未能检索QQ Connect的OpenId(返回状态码:{openIdResponse.StatusCode}),请检查access_token是正确。");
            }
            var json   = JObject.Parse(ConvertToJson(await openIdResponse.Content.ReadAsStringAsync()));
            var openId = GetOpenId(json);

            //获取用户信息
            var parameters = new Dictionary <string, string>
            {
                { "openid", openId },
                { "access_token", tokens.AccessToken },
                { "oauth_consumer_key", Options.ClientId }
            };
            var userInformationEndpoint = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, parameters);
            var userInformationResponse = await Backchannel.GetAsync(userInformationEndpoint, Context.RequestAborted);

            if (!userInformationResponse.IsSuccessStatusCode)
            {
                throw new HttpRequestException($"未能检索QQ Connect的用户信息(返回状态码:{userInformationResponse.StatusCode}),请检查参数是否正确。");
            }

            var payload = JObject.Parse(await userInformationResponse.Content.ReadAsStringAsync());

            //identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, openId, ClaimValueTypes.String, Options.ClaimsIssuer));
            payload.Add("openid", openId);
            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions();
            await Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
                                                                               [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            HttpRequestMessage  request  = null;
            HttpResponseMessage response = null;

            try
            {
                request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
                request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);

                response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

                if (!response.IsSuccessStatusCode)
                {
                    Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                    "returned a {Status} response with the following payload: {Headers} {Body}.",
                                    /* Status: */ response.StatusCode,
                                    /* Headers: */ response.Headers.ToString(),
                                    /* Body: */ await response.Content.ReadAsStringAsync());

                    throw new HttpRequestException("An error occurred while retrieving the user profile.");
                }

                var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

                var principal = new ClaimsPrincipal(identity);
                var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);
                context.RunClaimActions(payload);

                await Options.Events.CreatingTicket(context);

                return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
            }
            finally
            {
                request?.Dispose();
                response?.Dispose();
            }
        }
Example #22
0
    protected override async Task <AuthenticationTicket> CreateTicketAsync(
        [NotNull] ClaimsIdentity identity,
        [NotNull] AuthenticationProperties properties,
        [NotNull] OAuthTokenResponse tokens)
    {
        string endpoint = Options.UserInformationEndpoint;

        if (Options.Fields.Count > 0)
        {
            endpoint = QueryHelpers.AddQueryString(endpoint, "fields[user]", string.Join(',', Options.Fields));
        }

        if (Options.Includes.Count > 0)
        {
            endpoint = QueryHelpers.AddQueryString(endpoint, "include", string.Join(',', Options.Includes));
        }

        using var request = new HttpRequestMessage(HttpMethod.Get, endpoint);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);

        using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

        if (!response.IsSuccessStatusCode)
        {
            await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);

            throw new HttpRequestException("An error occurred while retrieving the user profile.");
        }

        using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));

        var principal = new ClaimsPrincipal(identity);
        var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

        context.RunClaimActions(payload.RootElement.GetProperty("data"));

        await Events.CreatingTicket(context);

        return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
    }
Example #23
0
    /// <inheritdoc />
    protected override async Task <AuthenticationTicket> CreateTicketAsync(
        [NotNull] ClaimsIdentity identity,
        [NotNull] AuthenticationProperties properties,
        [NotNull] OAuthTokenResponse tokens)
    {
        var contextId = await ProcessIdTokenAndGetContactIdentifierAsync(tokens, properties, identity);

        if (string.IsNullOrEmpty(contextId))
        {
            throw new InvalidOperationException("An error occurred trying to obtain the context identifier from the current user's identity claims.");
        }

        // Add contextId to the Options.UserInformationEndpoint (https://sod.superoffice.com/{0}/api/v1/user/currentPrincipal).
        var userInfoEndpoint = string.Format(CultureInfo.InvariantCulture, Options.UserInformationEndpoint, contextId);

        // Get the SuperOffice user principal.
        using var request = new HttpRequestMessage(HttpMethod.Get, userInfoEndpoint);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);

        using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

        if (!response.IsSuccessStatusCode)
        {
            await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);

            throw new HttpRequestException($"An error occurred when retrieving SuperOffice user information ({response.StatusCode}).");
        }

        using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));

        var principal = new ClaimsPrincipal(identity);

        var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

        context.RunClaimActions();

        await Events.CreatingTicket(context);

        return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
    }
        /// <summary>
        /// OAuth 'OnCreatingTicket' Handler
        /// Executed upon a successful external login.
        /// </summary>
        public static async Task HandleCreatingTicket(OAuthCreatingTicketContext context, GetClaimsCallback getClaimsCallback = null)
        {
            var oauthUser = await context.GetUser <TOAuthUser>();

            var logger             = context.Get <ILogger <TOAuthUser> >();
            var metadataConductor  = context.Get <IRepositoryConductor <TUserMetadata> >();
            var userConductor      = context.Get <IRepositoryConductor <TUser> >();
            var userLoginConductor = context.Get <IRepositoryConductor <TUserLogin> >();

            logger.LogInformation($"Authentication request for {oauthUser.UserMetadataName} {nameof(TUser)} '{oauthUser.Id}' - {oauthUser.Email}");

            var user      = FindOrCreateUser(oauthUser, userConductor, metadataConductor, logger);
            var userLogin = CreateLogin(userLoginConductor, user, context.Request, logger);

            if (getClaimsCallback != null)
            {
                context.Identity.AddClaims(getClaimsCallback(user, userLogin));
            }

            context.RunClaimActions();
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
        {
            var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);

            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);

            var response = await Backchannel.SendAsync(request, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                throw new HttpRequestException($"An error occurred when retrieving Microsoft user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Microsoft Account API is enabled.");
            }

            using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

            context.RunClaimActions();
            await Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
        }
Example #26
0
        protected override async Task <AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
        {
            var endpoint = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, "access_token", tokens.AccessToken);

            var response = await Backchannel.GetAsync(endpoint, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                throw new HttpRequestException(string.Format(CultureInfo.CurrentCulture, Resources.Exception_PodcastRetrievalFailure, response.StatusCode));
            }

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions();

            await Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
        protected override async Task <AuthenticationTicket> CreateTicketAsync(
            ClaimsIdentity identity,
            Microsoft.AspNetCore.Authentication.AuthenticationProperties properties,
            OAuthTokenResponse tokens)
        {
            var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);

            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);

            var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("Произошла ошибка при получении профиля пользователя: удаленный сервер " +
                                "вернул {Status} ответ со следующей информацией: {Headers} {Body}.",
                                response.StatusCode,
                                response.Headers.ToString(),
                                await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("Произошла ошибка при получении профиля пользователя.");
            }

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            identity.AddOptionalClaim(ClaimTypes.NameIdentifier, payload.Value <string>("id"), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.Name, payload.Value <string>("login"), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.Surname, payload.Value <string>("last_name"), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.GivenName, payload.Value <string>("first_name"), Options.ClaimsIssuer)
            .AddOptionalClaim(ClaimTypes.Email, payload.Value <JArray>("emails")?[0]?.Value <string>(), Options.ClaimsIssuer);


            var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions();

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }
Example #28
0
    protected override async Task <AuthenticationTicket> CreateTicketAsync(
        [NotNull] ClaimsIdentity identity,
        [NotNull] AuthenticationProperties properties,
        [NotNull] OAuthTokenResponse tokens)
    {
        // Note: the ArcGIS API doesn't support content negotiation via headers.
        var parameters = new Dictionary <string, string?>
        {
            ["f"]     = "json",
            ["token"] = tokens.AccessToken,
        };

        var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, parameters !);

        using var request = new HttpRequestMessage(HttpMethod.Get, address);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        // Request the token
        using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

        using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));

        // Note: error responses always return 200 status codes.
        if (payload.RootElement.TryGetProperty("error", out var error))
        {
            // See https://developers.arcgis.com/authentication/server-based-user-logins/ for more information
            Log.UserProfileError(Logger, error.GetString("code"), error.GetString("message"));

            throw new InvalidOperationException("An error occurred while retrieving the user profile.");
        }

        var principal = new ClaimsPrincipal(identity);
        var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

        context.RunClaimActions();

        await Events.CreatingTicket(context);

        return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
    }
Example #29
0
        protected override async Task <AuthenticationTicket> CreateTicketAsync(
            [NotNull] ClaimsIdentity identity,
            [NotNull] AuthenticationProperties properties,
            [NotNull] OAuthTokenResponse tokens)
        {
            using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            request.Headers.Authorization = new AuthenticationHeaderValue("bearer", tokens.AccessToken);

            // When a custom user agent is specified in the options, add it to the request headers
            // to override the default (generic) user agent used by the OAuth2 base middleware.
            if (!string.IsNullOrEmpty(Options.UserAgent))
            {
                request.Headers.UserAgent.ParseAdd(Options.UserAgent);
            }

            using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync(Context.RequestAborted));

                throw new HttpRequestException("An error occurred while retrieving the user profile.");
            }

            using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));

            var principal = new ClaimsPrincipal(identity);
            var context   = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);

            context.RunClaimActions();

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal !, context.Properties, Scheme.Name));
        }
Example #30
0
        protected override async Task <AuthenticationTicket> CreateTicketAsync([NotNull] ClaimsIdentity identity,
                                                                               [NotNull] AuthenticationProperties properties, [NotNull] OAuthTokenResponse tokens)
        {
            var userId  = tokens.Response.SelectToken("user").Value <string>("id");
            var request = new HttpRequestMessage(HttpMethod.Get, $"{Options.UserInformationEndpoint}api/v1/users/{userId}/profile");

            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);

            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("An error occurred while retrieving the user profile: the remote server " +
                                "returned a {Status} response with the following payload: {Headers} {Body}.",
                                /* Status: */ response.StatusCode,
                                /* Headers: */ response.Headers.ToString(),
                                /* Body: */ await response.Content.ReadAsStringAsync());

                throw new HttpRequestException("An error occurred while retrieving the user from the Canvas identity service.");
            }

            var payload   = JObject.Parse(await response.Content.ReadAsStringAsync());
            var principal = new ClaimsPrincipal(identity);

            var roles = await GetAccountRolesForUserAsync(Backchannel, "1", userId, tokens.AccessToken);

            foreach (var role in roles)
            {
                identity.AddClaim(new Claim(ClaimTypes.Role, role));
            }

            var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload);

            context.RunClaimActions(payload);

            await Options.Events.CreatingTicket(context);

            return(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
        }