public async void SaveCustomData()
        {
            IsBusy       = true;
            ProgressInfo = "Getting access token...";
            string accessToken = await accessTokenService.GetAccessTokenAsync(ClientId, ClientSecret, TokenEndpoint);

            ProgressInfo = "Saving data...";
            MapProduct map = new MapProduct
            {
                ProductId       = ProductIdForUpdate,
                EanBarcode      = CustomEan,
                Index           = CustomIndex,
                ProductFullName = CustomName
            };

            string result = await argipApiData.UpdateProductAsync(BaseApiAddress + @"v1/Products", accessToken, map);

            IsBusy       = false;
            ProgressInfo = result;
            ClearFields();
        }
Beispiel #2
0
        /// <summary>
        /// Handler for creating a line item.
        /// </summary>
        /// <returns>The result.</returns>
        public async Task <IActionResult> OnPostCreateLineItemAsync([FromForm(Name = "id_token")] string idToken)
        {
            if (idToken.IsMissing())
            {
                Error = $"{nameof(idToken)} is missing.";
                return(Page());
            }

            var handler = new JwtSecurityTokenHandler();
            var jwt     = handler.ReadJwtToken(idToken);

            LtiRequest = new LtiResourceLinkRequest(jwt.Payload);

            var tokenResponse = await _accessTokenService.GetAccessTokenAsync(
                Request.GetUri().GetLeftPart(UriPartial.Authority),
                LtiRequest.Iss,
                Constants.LtiScopes.Ags.LineItem);

            // The IMS reference implementation returns "Created" with success.
            if (tokenResponse.IsError && tokenResponse.Error != "Created")
            {
                Error = tokenResponse.Error;
                return(Page());
            }

            var httpClient = _httpClientFactory.CreateClient();

            httpClient.SetBearerToken(tokenResponse.AccessToken);
            httpClient.DefaultRequestHeaders.Accept
            .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItem));

            try
            {
                var lineItem = new LineItem
                {
                    EndDateTime    = DateTime.UtcNow.AddMonths(3),
                    Label          = LtiRequest.ResourceLink.Title,
                    ResourceLinkId = LtiRequest.ResourceLink.Id,
                    ResourceId     = Guid.NewGuid().ToString(),
                    ScoreMaximum   = 100,
                    StartDateTime  = DateTime.UtcNow
                };

                using (var response = await httpClient.PostAsync(
                           LtiRequest.AssignmentGradeServices.LineItemsUrl,
                           new StringContent(JsonConvert.SerializeObject(lineItem), Encoding.UTF8, Constants.MediaTypes.LineItem)))
                {
                    if (!response.IsSuccessStatusCode)
                    {
                        Error = response.ReasonPhrase;
                        return(Page());
                    }
                }
            }
            catch (Exception e)
            {
                Error = e.Message;
                return(Page());
            }

            return(Relaunch(
                       LtiRequest.Iss,
                       LtiRequest.UserId,
                       LtiRequest.ResourceLink.Id,
                       LtiRequest.Context.Id));
        }
Beispiel #3
0
        public async Task <IViewComponentResult> InvokeAsync(string idToken)
        {
            var model = new LineItemsModel(idToken);

            if (idToken.IsMissing())
            {
                model.Status = $"{nameof(idToken)} is missing.";
                return(View(model));
            }

            var handler = new JwtSecurityTokenHandler();
            var token   = handler.ReadJwtToken(idToken);

            model.LtiRequest = new LtiResourceLinkRequest(token.Payload);

            if (model.LtiRequest.AssignmentGradeServices == null)
            {
                model.Status = "Assignment and Grade Services not defined.";
                return(View(model));
            }
            model.LineItemUrl = model.LtiRequest.AssignmentGradeServices.LineItemUrl;

            var tokenResponse = await _accessTokenService.GetAccessTokenAsync(
                model.LtiRequest.Iss,
                string.Join(" ",
                            Constants.LtiScopes.Ags.LineItem,
                            Constants.LtiScopes.Ags.ResultReadonly,
                            Constants.LtiScopes.Nrps.MembershipReadonly));

            // The IMS reference implementation returns "Created" with success.
            if (tokenResponse.IsError && tokenResponse.Error != "Created")
            {
                model.Status = tokenResponse.Error;
                return(View(model));
            }

            // Get all the line items
            try
            {
                var httpClient = _httpClientFactory.CreateClient();
                httpClient.SetBearerToken(tokenResponse.AccessToken);

                httpClient.DefaultRequestHeaders.Accept
                .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItemContainer));

                using (var response = await httpClient.GetAsync(model.LtiRequest.AssignmentGradeServices?.LineItemsUrl))
                {
                    if (!response.IsSuccessStatusCode)
                    {
                        model.Status = response.ReasonPhrase;
                        return(View(model));
                    }

                    var content = await response.Content.ReadAsStringAsync();

                    model.LineItems = JsonConvert.DeserializeObject <List <LineItem> >(content)
                                      .Select(i => new MyLineItem
                    {
                        AgsLineItem = i,
                        Header      = i.Label ?? $"Tag: {i.Tag}"
                    })
                                      .ToList();
                }
            }
            catch (Exception e)
            {
                model.Status = e.Message;
                return(View());
            }

            // Get all the members of the course
            model.Members = new Dictionary <string, string>();

            try
            {
                var httpClient = _httpClientFactory.CreateClient();
                httpClient.SetBearerToken(tokenResponse.AccessToken);

                httpClient.DefaultRequestHeaders.Accept.Clear();
                httpClient.DefaultRequestHeaders.Accept
                .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.MembershipContainer));

                using (var response = await httpClient.GetAsync(model.LtiRequest.NamesRoleService.ContextMembershipUrl))
                {
                    if (!response.IsSuccessStatusCode)
                    {
                        model.Status = response.ReasonPhrase;
                        return(View(model));
                    }

                    var content = await response.Content.ReadAsStringAsync();

                    var membership = JsonConvert.DeserializeObject <MembershipContainer>(content);
                    foreach (var member in membership.Members.OrderBy(m => m.FamilyName).ThenBy(m => m.GivenName))
                    {
                        if (!model.Members.ContainsKey(member.UserId))
                        {
                            model.Members.Add(member.UserId, $"{member.FamilyName}, {member.GivenName}");
                        }
                    }
                }
            }
            catch (Exception e)
            {
                model.Status = e.Message;
                return(View(model));
            }

            // Get all the results
            try
            {
                var httpClient = _httpClientFactory.CreateClient();
                httpClient.SetBearerToken(tokenResponse.AccessToken);

                httpClient.DefaultRequestHeaders.Accept.Clear();
                httpClient.DefaultRequestHeaders.Accept
                .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.ResultContainer));

                foreach (var lineItem in model.LineItems)
                {
                    using (var response = await httpClient.GetAsync(lineItem.AgsLineItem.Id.EnsureTrailingSlash() + "results"))
                    {
                        if (!response.IsSuccessStatusCode)
                        {
                            model.Status = response.ReasonPhrase;
                            return(View(model));
                        }

                        var content = await response.Content.ReadAsStringAsync();

                        lineItem.Results = JsonConvert.DeserializeObject <ResultContainer>(content);
                    }
                }
            }
            catch (Exception e)
            {
                model.Status = e.Message;
            }

            return(View(model));
        }
Beispiel #4
0
        /// <summary>
        /// Handle the LTI POST request from the Authorization Server.
        /// </summary>
        /// <returns></returns>
        public async Task <IActionResult> OnPostAsync(
            string platformId,
            [FromForm(Name = "id_token")] string idToken,
            [FromForm(Name = "scope")] string scope = null,
            [FromForm(Name = "state")] string state = null,
            [FromForm(Name = "session_state")] string sessionState = null)
        {
            // Authenticate the request starting at step 5 in the OpenId Implicit Flow
            // See https://www.imsglobal.org/spec/security/v1p0/#platform-originating-messages
            // See https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowSteps

            // The Platform MUST send the id_token via the OAuth 2 Form Post
            // See https://www.imsglobal.org/spec/security/v1p0/#successful-authentication
            // See http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html

            if (string.IsNullOrEmpty(idToken))
            {
                Error = "id_token is missing or empty";
                return(Page());
            }

            var handler = new JwtSecurityTokenHandler();

            if (!handler.CanReadToken(idToken))
            {
                Error = "Cannot read id_token";
                return(Page());
            }

            var jwt = handler.ReadJwtToken(idToken);

            JwtHeader = jwt.Header;

            var messageType = jwt.Claims.SingleOrDefault(c => c.Type == Constants.LtiClaims.MessageType)?.Value;

            if (messageType.IsMissing())
            {
                Error = $"{Constants.LtiClaims.MessageType} claim is missing.";
                return(Page());
            }

            // Authentication Response Validation
            // See https://www.imsglobal.org/spec/security/v1p0/#authentication-response-validation

            // The ID Token MUST contain a nonce Claim.
            var nonce = jwt.Claims.SingleOrDefault(c => c.Type == "nonce")?.Value;

            if (string.IsNullOrEmpty(nonce))
            {
                Error = "Nonce is missing from request.";
                return(Page());
            }

            // If the launch was initiated with a 3rd party login, then there will be a state
            // entry for the nonce.
            var memorizedState = _stateContext.GetState(nonce);

            if (memorizedState == null)
            {
                Error = "Invalid nonce. Possible request replay.";
                return(Page());
            }

            // The state should be echoed back by the AS without modification
            if (memorizedState.Value != state)
            {
                Error = "Invalid state.";
                return(Page());
            }

            // Look for the platform with platformId in the redirect URI
            var platform = await _context.GetPlatformByPlatformId(platformId);

            if (platform == null)
            {
                Error = "Unknown platform.";
                return(Page());
            }

            // Using the JwtSecurityTokenHandler.ValidateToken method, validate four things:
            //
            // 1. The Issuer Identifier for the Platform MUST exactly match the value of the iss
            //    (Issuer) Claim (therefore the Tool MUST previously have been made aware of this
            //    identifier.
            // 2. The Tool MUST Validate the signature of the ID Token according to JSON Web Signature
            //    RFC 7515, Section 5; using the Public Key for the Platform which collected offline.
            // 3. The Tool MUST validate that the aud (audience) Claim contains its client_id value
            //    registered as an audience with the Issuer identified by the iss (Issuer) Claim. The
            //    aud (audience) Claim MAY contain an array with more than one element. The Tool MUST
            //    reject the ID Token if it does not list the client_id as a valid audience, or if it
            //    contains additional audiences not trusted by the Tool.
            // 4. The current time MUST be before the time represented by the exp Claim;

            RSAParameters rsaParameters;

            try
            {
                var httpClient = _httpClientFactory.CreateClient();
                var keySetJson = await httpClient.GetStringAsync(platform.JwkSetUrl);

                var keySet = JsonConvert.DeserializeObject <JsonWebKeySet>(keySetJson);
                var key    = keySet.Keys.SingleOrDefault(k => k.Kid == jwt.Header.Kid);
                if (key == null)
                {
                    Error = "No matching key found.";
                    return(Page());
                }

                rsaParameters = new RSAParameters
                {
                    Modulus  = Base64UrlEncoder.DecodeBytes(key.N),
                    Exponent = Base64UrlEncoder.DecodeBytes(key.E)
                };
            }
            catch (Exception e)
            {
                Error = e.Message;
                return(Page());
            }

            var validationParameters = new TokenValidationParameters
            {
                ValidateTokenReplay      = true,
                ValidateAudience         = true,
                ValidateIssuer           = true,
                RequireSignedTokens      = true,
                ValidateIssuerSigningKey = true,

                ValidAudience    = platform.ClientId,
                ValidIssuer      = platform.Issuer,
                IssuerSigningKey = new RsaSecurityKey(rsaParameters),

                ValidateLifetime = true,
                ClockSkew        = TimeSpan.FromMinutes(5.0)
            };

            try
            {
                handler.ValidateToken(idToken, validationParameters, out _);
            }
            catch (Exception e)
            {
                Error = e.Message;
                return(Page());
            }

            if (messageType == Constants.Lti.LtiDeepLinkingRequestMessageType)
            {
                return(Post("/Catalog", new { idToken }));
            }

            IdToken    = idToken;
            LtiRequest = new LtiResourceLinkRequest(jwt.Payload);

            var tokenResponse = await _accessTokenService.GetAccessTokenAsync(
                LtiRequest.Iss,
                Constants.LtiScopes.Ags.LineItem);

            var lineItemClient = _httpClientFactory.CreateClient();

            lineItemClient.SetBearerToken(tokenResponse.AccessToken);
            lineItemClient.DefaultRequestHeaders.Accept
            .Add(new MediaTypeWithQualityHeaderValue(Constants.MediaTypes.LineItem));

            var resultsUrl      = $"{LtiRequest.AssignmentGradeServices.LineItemUrl}/{Constants.ServiceEndpoints.Ags.ResultsService}";
            var resultsResponse = await lineItemClient.GetAsync(resultsUrl);

            var resultsContent = await resultsResponse.Content.ReadAsStringAsync();

            var results = JsonConvert.DeserializeObject <ResultContainer>(resultsContent);

            Results = results;

            var lineItemResponse = await lineItemClient.GetAsync(LtiRequest.AssignmentGradeServices.LineItemUrl);

            var lineItemContent = await lineItemResponse.Content.ReadAsStringAsync();

            var lineItem = JsonConvert.DeserializeObject <LineItem>(lineItemContent);

            LineItem = lineItem;

            return(Page());
        }