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(); }
/// <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)); }
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)); }
/// <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()); }