HandleResponseAsync(string clientId, string deploymentId, string? contextId, DeepLinkResponse response, CancellationToken cancellationToken = default) =>
+ Task.FromResult(Results.Content(@$"
This is the end of the Deep Linking flow. Please override the {nameof(ILti13DeepLinkingHandler)} for a better experience.
",
- MediaTypeNames.Text.Html));
- }
+ MediaTypeNames.Text.Html));
}
diff --git a/NP.Lti13Platform.DeepLinking/Startup.cs b/NP.Lti13Platform.DeepLinking/Startup.cs
index 05d5486..03ec3f5 100644
--- a/NP.Lti13Platform.DeepLinking/Startup.cs
+++ b/NP.Lti13Platform.DeepLinking/Startup.cs
@@ -64,23 +64,11 @@ public static IEndpointRouteBuilder UseLti13PlatformDeepLinking(this IEndpointRo
async ([FromForm] DeepLinkResponseRequest request, string? contextId, ILogger logger, ILti13TokenConfigService tokenService, ILti13CoreDataService coreDataService, ILti13DeepLinkingDataService deepLinkingDataService, ILti13DeepLinkingConfigService deepLinkingService, ILti13DeepLinkingHandler deepLinkingHandler, CancellationToken cancellationToken) =>
{
const string DEEP_LINKING_SPEC = "https://www.imsglobal.org/spec/lti-dl/v2p0/#deep-linking-response-message";
- const string INVALID_CLIENT = "invalid_client";
const string INVALID_REQUEST = "invalid_request";
- const string JWT_REQUIRED = "JWT is required";
- const string DEPLOYMENT_ID_REQUIRED = "deployment_id is required";
- const string CLIENT_ID_REQUIRED = "client_id is required";
- const string DEPLOYMENT_ID_INVALID = "deployment_id is invalid";
- const string MESSAGE_TYPE_INVALID = "message_type is invalid";
- const string VERSION_INVALID = "version is invalid";
- const string UNKNOWN = "unknown";
- const string TYPE = "type";
- const string VERSION = "1.3.0";
- const string LTI_DEEP_LINKING_RESPONSE = "LtiDeepLinkingResponse";
- const string DEPLOYMENT_ID_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/deployment_id";
if (string.IsNullOrWhiteSpace(request.Jwt))
{
- return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = JWT_REQUIRED, Error_Uri = DEEP_LINKING_SPEC });
+ return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = "JWT is required", Error_Uri = DEEP_LINKING_SPEC });
}
var jwt = new JsonWebToken(request.Jwt);
@@ -89,18 +77,18 @@ public static IEndpointRouteBuilder UseLti13PlatformDeepLinking(this IEndpointRo
var tool = await coreDataService.GetToolAsync(clientId, cancellationToken);
if (tool?.Jwks == null)
{
- return Results.NotFound(new { Error = INVALID_CLIENT, Error_Description = CLIENT_ID_REQUIRED, Error_Uri = DEEP_LINKING_SPEC });
+ return Results.NotFound(new { Error = "invalid_client", Error_Description = "client_id is required", Error_Uri = DEEP_LINKING_SPEC });
}
- if (!jwt.TryGetClaim(DEPLOYMENT_ID_CLAIM, out var deploymentIdClaim))
+ if (!jwt.TryGetClaim("https://purl.imsglobal.org/spec/lti/claim/deployment_id", out var deploymentIdClaim))
{
- return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = DEPLOYMENT_ID_REQUIRED, Error_Uri = DEEP_LINKING_SPEC });
+ return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = "deployment_id is required", Error_Uri = DEEP_LINKING_SPEC });
}
var deployment = await coreDataService.GetDeploymentAsync(deploymentIdClaim.Value, cancellationToken);
if (deployment == null || deployment.ToolId != tool.Id)
{
- return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = DEPLOYMENT_ID_INVALID, Error_Uri = DEEP_LINKING_SPEC });
+ return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = "deployment_id is invalid", Error_Uri = DEEP_LINKING_SPEC });
}
var tokenConfig = await tokenService.GetTokenConfigAsync(tool.ClientId, cancellationToken);
@@ -117,14 +105,14 @@ public static IEndpointRouteBuilder UseLti13PlatformDeepLinking(this IEndpointRo
return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = validatedToken.Exception.Message, Error_Uri = DEEP_LINKING_SPEC });
}
- if (!validatedToken.Claims.TryGetValue("https://purl.imsglobal.org/spec/lti/claim/message_type", out var messageType) || (string)messageType != LTI_DEEP_LINKING_RESPONSE)
+ if (!validatedToken.Claims.TryGetValue("https://purl.imsglobal.org/spec/lti/claim/message_type", out var messageType) || (string)messageType != "LtiDeepLinkingResponse")
{
- return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = MESSAGE_TYPE_INVALID, Error_Uri = DEEP_LINKING_SPEC });
+ return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = "message_type is invalid", Error_Uri = DEEP_LINKING_SPEC });
}
- if (!validatedToken.Claims.TryGetValue("https://purl.imsglobal.org/spec/lti/claim/version", out var version) || (string)version != VERSION)
+ if (!validatedToken.Claims.TryGetValue("https://purl.imsglobal.org/spec/lti/claim/version", out var version) || (string)version != "1.3.0")
{
- return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = VERSION_INVALID, Error_Uri = DEEP_LINKING_SPEC });
+ return Results.BadRequest(new { Error = INVALID_REQUEST, Error_Description = "version is invalid", Error_Uri = DEEP_LINKING_SPEC });
}
var deepLinkingConfig = await deepLinkingService.GetConfigAsync(tool.ClientId, cancellationToken);
@@ -132,7 +120,7 @@ public static IEndpointRouteBuilder UseLti13PlatformDeepLinking(this IEndpointRo
List<(ContentItem ContentItem, LtiResourceLinkContentItem? LtiResourceLink)> contentItems = validatedToken.ClaimsIdentity.FindAll("https://purl.imsglobal.org/spec/lti-dl/claim/content_items")
.Select((x, ix) =>
{
- var type = JsonDocument.Parse(x.Value).RootElement.GetProperty(TYPE).GetString() ?? UNKNOWN;
+ var type = JsonDocument.Parse(x.Value).RootElement.GetProperty("type").GetString() ?? "unknown";
var customItem = (ContentItem)JsonSerializer.Deserialize(x.Value, deepLinkingConfig.ContentItemTypes[(tool.ClientId, type)])!;
return (customItem, type == ContentItemType.LtiResourceLink ? JsonSerializer.Deserialize(x.Value) : null);
diff --git a/NP.Lti13Platform.DynamicRegistration/Class1.cs b/NP.Lti13Platform.DynamicRegistration/Class1.cs
deleted file mode 100644
index 5a1a472..0000000
--- a/NP.Lti13Platform.DynamicRegistration/Class1.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace NP.Lti13Platform.DynamicRegistration
-{
- public class Class1
- {
-
- }
-}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Configs/EndpointsConfig.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Configs/EndpointsConfig.cs
index 15b7c04..a872fa1 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/Configs/EndpointsConfig.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/Configs/EndpointsConfig.cs
@@ -1,12 +1,11 @@
-namespace NP.Lti13Platform.NameRoleProvisioningServices.Configs
+namespace NP.Lti13Platform.NameRoleProvisioningServices.Configs;
+
+public class EndpointsConfig
{
- public class EndpointsConfig
- {
- ///
- /// Endpoint used to get a list of members in the context.
- /// Must include route parameters for {deploymentId} and {contextId}.
- ///
- /// Default: /lti13/{deploymentId}/{contextId}/memberships
- public string NamesAndRoleProvisioningServicesUrl { get; set; } = "/lti13/{deploymentId}/{contextId}/memberships";
- }
+ ///
+ /// Endpoint used to get a list of members in the context.
+ /// Must include route parameters for {deploymentId} and {contextId}.
+ ///
+ /// Default: /lti13/{deploymentId}/{contextId}/memberships
+ public string NamesAndRoleProvisioningServicesUrl { get; set; } = "/lti13/{deploymentId}/{contextId}/memberships";
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Constants.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Constants.cs
index f6da4b6..5e14b16 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/Constants.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/Constants.cs
@@ -2,16 +2,16 @@
{
internal static class RouteNames
{
- public const string GET_MEMBERSHIPS = "GET_MEMBERSHIPS";
+ public static readonly string GET_MEMBERSHIPS = "GET_MEMBERSHIPS";
}
internal static class Lti13ContentTypes
{
- internal const string MembershipContainer = "application/vnd.ims.lti-nrps.v2.membershipcontainer+json";
+ internal static readonly string MembershipContainer = "application/vnd.ims.lti-nrps.v2.membershipcontainer+json";
}
public static class Lti13ServiceScopes
{
- public const string MembershipReadOnly = "https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly";
+ public static readonly string MembershipReadOnly = "https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly";
}
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessage.cs b/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessage.cs
index e42b6e4..933c264 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessage.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessage.cs
@@ -1,10 +1,9 @@
using System.Text.Json.Serialization;
-namespace NP.Lti13Platform.NameRoleProvisioningServices
+namespace NP.Lti13Platform.NameRoleProvisioningServices;
+
+public class NameRoleProvisioningMessage
{
- public class NameRoleProvisioningMessage
- {
- [JsonPropertyName("https://purl.imsglobal.org/spec/lti/claim/message_type")]
- public required string MessageType { get; set; }
- }
+ [JsonPropertyName("https://purl.imsglobal.org/spec/lti/claim/message_type")]
+ public required string MessageType { get; set; }
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessageTypeResolver.cs b/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessageTypeResolver.cs
index 035594c..819ea81 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessageTypeResolver.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/NameRoleProvisioningMessageTypeResolver.cs
@@ -1,36 +1,35 @@
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
-namespace NP.Lti13Platform.NameRoleProvisioningServices
+namespace NP.Lti13Platform.NameRoleProvisioningServices;
+
+internal class NameRoleProvisioningMessageTypeResolver : DefaultJsonTypeInfoResolver
{
- internal class NameRoleProvisioningMessageTypeResolver : DefaultJsonTypeInfoResolver
+ private static readonly HashSet derivedTypes = [];
+
+ public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
- private static readonly HashSet derivedTypes = [];
+ var jsonTypeInfo = base.GetTypeInfo(type, options);
- public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
+ var baseType = typeof(NameRoleProvisioningMessage);
+ if (jsonTypeInfo.Type == baseType)
{
- var jsonTypeInfo = base.GetTypeInfo(type, options);
-
- var baseType = typeof(NameRoleProvisioningMessage);
- if (jsonTypeInfo.Type == baseType)
+ jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
{
- jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
- {
- IgnoreUnrecognizedTypeDiscriminators = true,
- };
+ IgnoreUnrecognizedTypeDiscriminators = true,
+ };
- foreach (var derivedType in derivedTypes)
- {
- jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derivedType));
- }
+ foreach (var derivedType in derivedTypes)
+ {
+ jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derivedType));
}
-
- return jsonTypeInfo;
}
- public static void AddDerivedType(Type type)
- {
- derivedTypes.Add(type);
- }
+ return jsonTypeInfo;
+ }
+
+ public static void AddDerivedType(Type type)
+ {
+ derivedTypes.Add(type);
}
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Populators/CustomPopulator.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Populators/CustomPopulator.cs
index 53d4ebe..3ce8553 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/Populators/CustomPopulator.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/Populators/CustomPopulator.cs
@@ -5,91 +5,90 @@
using NP.Lti13Platform.Core.Services;
using System.Text.Json.Serialization;
-namespace NP.Lti13Platform.NameRoleProvisioningServices.Populators
+namespace NP.Lti13Platform.NameRoleProvisioningServices.Populators;
+
+public interface ICustomMessage
{
- public interface ICustomMessage
- {
- [JsonPropertyName("https://purl.imsglobal.org/spec/lti/claim/custom")]
- public IDictionary? Custom { get; set; }
- }
+ [JsonPropertyName("https://purl.imsglobal.org/spec/lti/claim/custom")]
+ public IDictionary? Custom { get; set; }
+}
+
+public class CustomPopulator(ILti13CoreDataService dataService) : Populator
+{
+ private static readonly IEnumerable LineItemAttemptGradeVariables = [
+ Lti13ResourceLinkVariables.AvailableUserStartDateTime,
+ Lti13ResourceLinkVariables.AvailableUserEndDateTime,
+ Lti13ResourceLinkVariables.SubmissionUserStartDateTime,
+ Lti13ResourceLinkVariables.SubmissionUserEndDateTime,
+ Lti13ResourceLinkVariables.LineItemUserReleaseDateTime];
- public class CustomPopulator(ILti13CoreDataService dataService) : Populator
+ public override async Task PopulateAsync(ICustomMessage obj, MessageScope scope, CancellationToken cancellationToken = default)
{
- private static readonly IEnumerable LineItemAttemptGradeVariables = [
- Lti13ResourceLinkVariables.AvailableUserStartDateTime,
- Lti13ResourceLinkVariables.AvailableUserEndDateTime,
- Lti13ResourceLinkVariables.SubmissionUserStartDateTime,
- Lti13ResourceLinkVariables.SubmissionUserEndDateTime,
- Lti13ResourceLinkVariables.LineItemUserReleaseDateTime];
+ var customDictionary = scope.Tool.Custom.Merge(scope.Deployment.Custom).Merge(scope.ResourceLink?.Custom);
- public override async Task PopulateAsync(ICustomMessage obj, MessageScope scope, CancellationToken cancellationToken = default)
+ if (customDictionary == null)
{
- var customDictionary = scope.Tool.Custom.Merge(scope.Deployment.Custom).Merge(scope.ResourceLink?.Custom);
-
- if (customDictionary == null)
- {
- return;
- }
+ return;
+ }
- IEnumerable mentoredUserIds = [];
- if (customDictionary.Values.Any(v => v == Lti13UserVariables.ScopeMentor) && scope.Context != null )
+ IEnumerable mentoredUserIds = [];
+ if (customDictionary.Values.Any(v => v == Lti13UserVariables.ScopeMentor) && scope.Context != null )
+ {
+ var membership = await dataService.GetMembershipAsync(scope.Context.Id, scope.UserScope.User.Id, cancellationToken);
+ if (membership != null && membership.Roles.Contains(Lti13ContextRoles.Mentor))
{
- var membership = await dataService.GetMembershipAsync(scope.Context.Id, scope.UserScope.User.Id, cancellationToken);
- if (membership != null && membership.Roles.Contains(Lti13ContextRoles.Mentor))
- {
- mentoredUserIds = membership.MentoredUserIds;
- }
+ mentoredUserIds = membership.MentoredUserIds;
}
+ }
- LineItem? lineItem = null;
- Attempt? attempt = null;
- Grade? grade = null;
- if (customDictionary.Values.Any(v => LineItemAttemptGradeVariables.Contains(v)) && scope.Context != null && scope.ResourceLink != null)
+ LineItem? lineItem = null;
+ Attempt? attempt = null;
+ Grade? grade = null;
+ if (customDictionary.Values.Any(v => LineItemAttemptGradeVariables.Contains(v)) && scope.Context != null && scope.ResourceLink != null)
+ {
+ var lineItems = await dataService.GetLineItemsAsync(scope.Deployment.Id, scope.Context.Id, pageIndex: 0, limit: 1, resourceLinkId: scope.ResourceLink.Id, cancellationToken: cancellationToken);
+ if (lineItems.TotalItems == 1)
{
- var lineItems = await dataService.GetLineItemsAsync(scope.Deployment.Id, scope.Context.Id, pageIndex: 0, limit: 1, resourceLinkId: scope.ResourceLink.Id, cancellationToken: cancellationToken);
- if (lineItems.TotalItems == 1)
- {
- lineItem = lineItems.Items.First();
-
- grade = await dataService.GetGradeAsync(lineItem.Id, scope.UserScope.User.Id, cancellationToken);
- }
+ lineItem = lineItems.Items.First();
- attempt = await dataService.GetAttemptAsync(scope.ResourceLink.Id, scope.UserScope.User.Id, cancellationToken);
+ grade = await dataService.GetGradeAsync(lineItem.Id, scope.UserScope.User.Id, cancellationToken);
}
- var customPermissions = await dataService.GetCustomPermissions(scope.Deployment.Id, cancellationToken);
+ attempt = await dataService.GetAttemptAsync(scope.ResourceLink.Id, scope.UserScope.User.Id, cancellationToken);
+ }
+
+ var customPermissions = await dataService.GetCustomPermissions(scope.Deployment.Id, cancellationToken);
- var dictionaryValues = customDictionary.ToList();
- foreach (var kvp in dictionaryValues)
+ var dictionaryValues = customDictionary.ToList();
+ foreach (var kvp in dictionaryValues)
+ {
+ var value = kvp.Value switch
{
- var value = kvp.Value switch
- {
- Lti13UserVariables.Id when customPermissions.UserId => scope.UserScope.User.Id,
- Lti13UserVariables.Image when customPermissions.UserImage => scope.UserScope.User.ImageUrl,
- Lti13UserVariables.Username when customPermissions.UserUsername => scope.UserScope.User.Username,
- Lti13UserVariables.Org when customPermissions.UserOrg => string.Join(',', scope.UserScope.User.Orgs),
- Lti13UserVariables.ScopeMentor when customPermissions.UserScopeMentor => string.Join(',', mentoredUserIds),
- Lti13UserVariables.GradeLevelsOneRoster when customPermissions.UserGradeLevelsOneRoster => string.Join(',', scope.UserScope.User.OneRosterGrades),
+ Lti13UserVariables.Id when customPermissions.UserId => scope.UserScope.User.Id,
+ Lti13UserVariables.Image when customPermissions.UserImage => scope.UserScope.User.ImageUrl,
+ Lti13UserVariables.Username when customPermissions.UserUsername => scope.UserScope.User.Username,
+ Lti13UserVariables.Org when customPermissions.UserOrg => string.Join(',', scope.UserScope.User.Orgs),
+ Lti13UserVariables.ScopeMentor when customPermissions.UserScopeMentor => string.Join(',', mentoredUserIds),
+ Lti13UserVariables.GradeLevelsOneRoster when customPermissions.UserGradeLevelsOneRoster => string.Join(',', scope.UserScope.User.OneRosterGrades),
- Lti13ResourceLinkVariables.AvailableUserStartDateTime when customPermissions.ResourceLinkAvailableUserStartDateTime => attempt?.AvailableStartDateTime?.ToString("O"),
- Lti13ResourceLinkVariables.AvailableUserEndDateTime when customPermissions.ResourceLinkAvailableUserEndDateTime => attempt?.AvailableEndDateTime?.ToString("O"),
- Lti13ResourceLinkVariables.SubmissionUserStartDateTime when customPermissions.ResourceLinkSubmissionUserStartDateTime => attempt?.SubmisstionStartDateTime?.ToString("O"),
- Lti13ResourceLinkVariables.SubmissionUserEndDateTime when customPermissions.ResourceLinkSubmissionUserEndDateTime => attempt?.SubmissionEndDateTime?.ToString("O"),
- Lti13ResourceLinkVariables.LineItemUserReleaseDateTime when customPermissions.ResourceLinkLineItemUserReleaseDateTime => grade?.ReleaseDateTime?.ToString("O"),
- _ => null
- };
+ Lti13ResourceLinkVariables.AvailableUserStartDateTime when customPermissions.ResourceLinkAvailableUserStartDateTime => attempt?.AvailableStartDateTime?.ToString("O"),
+ Lti13ResourceLinkVariables.AvailableUserEndDateTime when customPermissions.ResourceLinkAvailableUserEndDateTime => attempt?.AvailableEndDateTime?.ToString("O"),
+ Lti13ResourceLinkVariables.SubmissionUserStartDateTime when customPermissions.ResourceLinkSubmissionUserStartDateTime => attempt?.SubmisstionStartDateTime?.ToString("O"),
+ Lti13ResourceLinkVariables.SubmissionUserEndDateTime when customPermissions.ResourceLinkSubmissionUserEndDateTime => attempt?.SubmissionEndDateTime?.ToString("O"),
+ Lti13ResourceLinkVariables.LineItemUserReleaseDateTime when customPermissions.ResourceLinkLineItemUserReleaseDateTime => grade?.ReleaseDateTime?.ToString("O"),
+ _ => null
+ };
- if (value == null)
- {
- customDictionary.Remove(kvp.Key);
- }
- else
- {
- customDictionary[kvp.Key] = value;
- }
+ if (value == null)
+ {
+ customDictionary.Remove(kvp.Key);
+ }
+ else
+ {
+ customDictionary[kvp.Key] = value;
}
-
- obj.Custom = obj.Custom.Merge(customDictionary);
}
+
+ obj.Custom = obj.Custom.Merge(customDictionary);
}
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Populators/ServiceEndpointsPopulator.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Populators/ServiceEndpointsPopulator.cs
index b5da5f3..b79875f 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/Populators/ServiceEndpointsPopulator.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/Populators/ServiceEndpointsPopulator.cs
@@ -4,37 +4,36 @@
using NP.Lti13Platform.NameRoleProvisioningServices.Services;
using System.Text.Json.Serialization;
-namespace NP.Lti13Platform.NameRoleProvisioningServices.Populators
+namespace NP.Lti13Platform.NameRoleProvisioningServices.Populators;
+
+public interface IServiceEndpoints
{
- public interface IServiceEndpoints
- {
- [JsonPropertyName("https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice")]
- public ServiceEndpoints? NamesRoleService { get; set; }
+ [JsonPropertyName("https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice")]
+ public ServiceEndpoints? NamesRoleService { get; set; }
- public class ServiceEndpoints
- {
- [JsonPropertyName("context_memberships_url")]
- public required string ContextMembershipsUrl { get; set; }
+ public class ServiceEndpoints
+ {
+ [JsonPropertyName("context_memberships_url")]
+ public required string ContextMembershipsUrl { get; set; }
- [JsonPropertyName("service_versions")]
- public required IEnumerable ServiceVersions { get; set; }
- }
+ [JsonPropertyName("service_versions")]
+ public required IEnumerable ServiceVersions { get; set; }
}
+}
- public class ServiceEndpointsPopulator(LinkGenerator linkGenerator, ILti13NameRoleProvisioningConfigService nameRoleProvisioningService) : Populator
+public class ServiceEndpointsPopulator(LinkGenerator linkGenerator, ILti13NameRoleProvisioningConfigService nameRoleProvisioningService) : Populator
+{
+ public override async Task PopulateAsync(IServiceEndpoints obj, MessageScope scope, CancellationToken cancellationToken = default)
{
- public override async Task PopulateAsync(IServiceEndpoints obj, MessageScope scope, CancellationToken cancellationToken = default)
+ if (scope.Tool.ServiceScopes.Contains(Lti13ServiceScopes.MembershipReadOnly) && !string.IsNullOrWhiteSpace(scope.Context?.Id))
{
- if (scope.Tool.ServiceScopes.Contains(Lti13ServiceScopes.MembershipReadOnly) && !string.IsNullOrWhiteSpace(scope.Context?.Id))
- {
- var config = await nameRoleProvisioningService.GetConfigAsync(scope.Tool.ClientId, cancellationToken);
+ var config = await nameRoleProvisioningService.GetConfigAsync(scope.Tool.ClientId, cancellationToken);
- obj.NamesRoleService = new IServiceEndpoints.ServiceEndpoints
- {
- ContextMembershipsUrl = linkGenerator.GetUriByName(RouteNames.GET_MEMBERSHIPS, new { deploymentId = scope.Deployment.Id, contextId = scope.Context.Id }, config.ServiceAddress.Scheme, new HostString(config.ServiceAddress.Authority)) ?? string.Empty,
- ServiceVersions = ["2.0"]
- };
- }
+ obj.NamesRoleService = new IServiceEndpoints.ServiceEndpoints
+ {
+ ContextMembershipsUrl = linkGenerator.GetUriByName(RouteNames.GET_MEMBERSHIPS, new { deploymentId = scope.Deployment.Id, contextId = scope.Context.Id }, config.ServiceAddress.Scheme, new HostString(config.ServiceAddress.Authority)) ?? string.Empty,
+ ServiceVersions = ["2.0"]
+ };
}
}
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Services/DefaultNameRoleProvisioningConfigService.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Services/DefaultNameRoleProvisioningConfigService.cs
index 703485d..ff4bf1e 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/Services/DefaultNameRoleProvisioningConfigService.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/Services/DefaultNameRoleProvisioningConfigService.cs
@@ -2,19 +2,18 @@
using Microsoft.Extensions.Options;
using NP.Lti13Platform.NameRoleProvisioningServices.Configs;
-namespace NP.Lti13Platform.NameRoleProvisioningServices.Services
+namespace NP.Lti13Platform.NameRoleProvisioningServices.Services;
+
+internal class DefaultNameRoleProvisioningConfigService(IOptionsMonitor config, IHttpContextAccessor httpContextAccessor) : ILti13NameRoleProvisioningConfigService
{
- internal class DefaultNameRoleProvisioningConfigService(IOptionsMonitor config, IHttpContextAccessor httpContextAccessor) : ILti13NameRoleProvisioningConfigService
+ public async Task GetConfigAsync(string clientId, CancellationToken cancellationToken = default)
{
- public async Task GetConfigAsync(string clientId, CancellationToken cancellationToken = default)
+ var servicesConfig = config.CurrentValue;
+ if (servicesConfig.ServiceAddress == ServicesConfig.DefaultUri)
{
- var servicesConfig = config.CurrentValue;
- if (servicesConfig.ServiceAddress == ServicesConfig.DefaultUri)
- {
- servicesConfig = servicesConfig with { ServiceAddress = new UriBuilder(httpContextAccessor.HttpContext?.Request.Scheme, httpContextAccessor.HttpContext?.Request.Host.Value).Uri };
- }
-
- return await Task.FromResult(servicesConfig);
+ servicesConfig = servicesConfig with { ServiceAddress = new UriBuilder(httpContextAccessor.HttpContext?.Request.Scheme, httpContextAccessor.HttpContext?.Request.Host.Value).Uri };
}
+
+ return await Task.FromResult(servicesConfig);
}
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Services/ILti13NameRoleProvisioningDataService.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Services/ILti13NameRoleProvisioningDataService.cs
index e0b78c8..2e16982 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/Services/ILti13NameRoleProvisioningDataService.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/Services/ILti13NameRoleProvisioningDataService.cs
@@ -5,6 +5,6 @@ namespace NP.Lti13Platform.NameRoleProvisioningServices.Services
public interface ILti13NameRoleProvisioningDataService
{
Task> GetMembershipsAsync(string deploymentId, string contextId, int pageIndex, int limit, string? role, string? resourceLinkId, DateTime? asOfDate = null, CancellationToken cancellationToken = default);
- Task> GetUserPermissionsAsync(string deploymentId, IEnumerable userIds, CancellationToken cancellationToken = default);
+ Task> GetUserPermissionsAsync(string deploymentId, IEnumerable userIds, CancellationToken cancellationToken = default);
}
}
diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs
index 2bd8881..677d627 100644
--- a/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs
+++ b/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs
@@ -112,13 +112,6 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices
routeBuilder.MapGet(config.NamesAndRoleProvisioningServicesUrl,
async (IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor, ILti13CoreDataService coreDataService, ILti13NameRoleProvisioningDataService nrpsDataService, LinkGenerator linkGenerator, string deploymentId, string contextId, string? role, string? rlid, int? limit, int pageIndex = 0, long? since = null, CancellationToken cancellationToken = default) =>
{
- const string RESOURCE_LINK_UNAVAILABLE = "resource link unavailable";
- const string RESOURCE_LINK_UNAVAILABLE_DESCRIPTION = "resource link does not exist in the context";
- const string RESOURCE_LINK_UNAVAILABLE_URI = "https://www.imsglobal.org/spec/lti-nrps/v2p0#access-restriction";
- const string ACTIVE = "Active";
- const string INACTIVE = "Inactive";
- const string DELETED = "Deleted";
-
var httpContext = httpContextAccessor.HttpContext!;
var context = await coreDataService.GetContextAsync(contextId, cancellationToken);
@@ -137,7 +130,7 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices
var deployment = await coreDataService.GetDeploymentAsync(deploymentId, cancellationToken);
if (deployment?.ToolId != tool.Id)
{
- return Results.BadRequest(new { Error = RESOURCE_LINK_UNAVAILABLE, Error_Description = RESOURCE_LINK_UNAVAILABLE_DESCRIPTION, Error_Uri = RESOURCE_LINK_UNAVAILABLE_URI });
+ return Results.NotFound();
}
var membersResponse = await nrpsDataService.GetMembershipsAsync(deploymentId, contextId, pageIndex, limit ?? int.MaxValue, role, rlid, cancellationToken: cancellationToken);
@@ -177,12 +170,12 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices
var resourceLink = await coreDataService.GetResourceLinkAsync(rlid, cancellationToken);
if (resourceLink == null || resourceLink.DeploymentId != deploymentId)
{
- return Results.BadRequest(new { Error = RESOURCE_LINK_UNAVAILABLE, Error_Description = RESOURCE_LINK_UNAVAILABLE_DESCRIPTION, Error_Uri = RESOURCE_LINK_UNAVAILABLE_URI });
+ return Results.BadRequest(new { Error = "resource link unavailable", Error_Description = "resource link does not exist in the context", Error_Uri = "https://www.imsglobal.org/spec/lti-nrps/v2p0#access-restriction" });
}
var messageTypes = LtiMessageTypes.ToDictionary(mt => mt.Key, mt => serviceProvider.GetKeyedServices(mt.Key));
- foreach (var currentUser in currentUsers)
+ foreach (var currentUser in currentUsers) // TODO: await in list
{
ICollection userMessages = [];
messages.Add(currentUser.User.Id, userMessages);
@@ -195,7 +188,7 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices
resourceLink,
MessageHint: null);
- foreach (var messageType in messageTypes)
+ foreach (var messageType in messageTypes) // TODO: await in list
{
var message = serviceProvider.GetKeyedService(messageType.Key);
@@ -203,7 +196,7 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices
{
message.MessageType = messageType.Key.Name;
- foreach (var populator in messageType.Value)
+ foreach (var populator in messageType.Value) // TODO: await in list
{
await populator.PopulateAsync(message, scope, cancellationToken);
}
@@ -218,7 +211,7 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices
var userPermissions = await nrpsDataService.GetUserPermissionsAsync(deployment.Id, usersWithRoles.Select(u => u.User.Id), cancellationToken);
- var users = usersWithRoles.Join(userPermissions, u => u.User.Id, p => p.UserId, (u, p) => (u.User, u.Membership, p.UserPermissions, u.IsCurrent));
+ var users = usersWithRoles.Join(userPermissions, u => u.User.Id, p => p.UserId, (u, p) => (u.User, u.Membership, UserPermissions: p, u.IsCurrent));
return Results.Json(new
{
@@ -242,9 +235,9 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices
picture = x.UserPermissions.Picture ? x.User.Picture : null,
status = x.Membership.Status switch
{
- MembershipStatus.Active when x.IsCurrent => ACTIVE,
- MembershipStatus.Inactive when x.IsCurrent => INACTIVE,
- _ => DELETED
+ MembershipStatus.Active when x.IsCurrent => "Active",
+ MembershipStatus.Inactive when x.IsCurrent => "Inactive",
+ _ => "Deleted"
},
message = messages.TryGetValue(x.User.Id, out var message) ? message : null
};
diff --git a/NP.Lti13Platform.ProctoringServices/Class1.cs b/NP.Lti13Platform.ProctoringServices/Class1.cs
deleted file mode 100644
index b35b84d..0000000
--- a/NP.Lti13Platform.ProctoringServices/Class1.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace NP.Lti13Platform.ProctoringServices
-{
- public class Class1
- {
-
- }
-}
diff --git a/NP.Lti13Platform.SubmissionReviewService/Class1.cs b/NP.Lti13Platform.SubmissionReviewService/Class1.cs
deleted file mode 100644
index eae271c..0000000
--- a/NP.Lti13Platform.SubmissionReviewService/Class1.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace NP.Lti13Platform.SubmissionReviewService
-{
- public class Class1
- {
-
- }
-}
diff --git a/NP.Lti13Platform.WebExample/Controllers/HomeController.cs b/NP.Lti13Platform.WebExample/Controllers/HomeController.cs
index 58c8195..168b034 100644
--- a/NP.Lti13Platform.WebExample/Controllers/HomeController.cs
+++ b/NP.Lti13Platform.WebExample/Controllers/HomeController.cs
@@ -4,52 +4,51 @@
using NP.Lti13Platform.Core.Services;
using NP.Lti13Platform.DeepLinking;
-namespace NP.Lti13Platform.WebExample.Controllers
+namespace NP.Lti13Platform.WebExample.Controllers;
+
+public class HomeController(ILogger logger, IUrlServiceHelper service, ILti13CoreDataService dataService) : Controller
{
- public class HomeController(ILogger logger, IUrlServiceHelper service, ILti13CoreDataService dataService) : Controller
+ public async Task Index(CancellationToken cancellationToken)
{
- public async Task Index(CancellationToken cancellationToken)
- {
- var tool = await dataService.GetToolAsync("clientId", cancellationToken);
- var deployment = await dataService.GetDeploymentAsync("deploymentId", cancellationToken);
- var context = await dataService.GetContextAsync("contextId", cancellationToken);
- var userId = "userId";
- var documentTarget = Lti13PresentationTargetDocuments.Window;
- var height = 200;
- var width = 250;
- var locale = "en-US";
+ var tool = await dataService.GetToolAsync("clientId", cancellationToken);
+ var deployment = await dataService.GetDeploymentAsync("deploymentId", cancellationToken);
+ var context = await dataService.GetContextAsync("contextId", cancellationToken);
+ var userId = "userId";
+ var documentTarget = Lti13PresentationTargetDocuments.Window;
+ var height = 200;
+ var width = 250;
+ var locale = "en-US";
- logger.LogInformation("LOGGING INFORMATION");
+ logger.LogInformation("LOGGING INFORMATION");
- return Results.Ok(new
- {
- deepLinkUrl = await service.GetDeepLinkInitiationUrlAsync(
+ return Results.Ok(new
+ {
+ deepLinkUrl = await service.GetDeepLinkInitiationUrlAsync(
+ tool!,
+ deployment!.Id,
+ userId,
+ false,
+ null,
+ context!.Id,
+ new DeepLinkSettingsOverride { Title = "TiTlE", Text = "TEXT", Data = "data" },
+ cancellationToken: cancellationToken),
+ resourceLinkUrls = DataService.ResourceLinks
+ .Select(async resourceLink => await service.GetResourceLinkInitiationUrlAsync(
tool!,
deployment!.Id,
+ context!.Id,
+ resourceLink,
userId,
false,
- null,
- context!.Id,
- new DeepLinkSettingsOverride { Title = "TiTlE", Text = "TEXT", Data = "data" },
- cancellationToken: cancellationToken),
- resourceLinkUrls = DataService.ResourceLinks
- .Select(async resourceLink => await service.GetResourceLinkInitiationUrlAsync(
- tool!,
- deployment!.Id,
- context!.Id,
- resourceLink,
- userId,
- false,
- launchPresentation: new LaunchPresentationOverride
- {
- DocumentTarget = documentTarget,
- Height = height,
- Width = width,
- Locale = locale
- },
- cancellationToken: cancellationToken))
- .Select(t => t.Result)
- });
- }
+ launchPresentation: new LaunchPresentationOverride
+ {
+ DocumentTarget = documentTarget,
+ Height = height,
+ Width = width,
+ Locale = locale
+ },
+ cancellationToken: cancellationToken))
+ .Select(t => t.Result)
+ });
}
}
diff --git a/NP.Lti13Platform.WebExample/DevTunnelHttpContextAccessor.cs b/NP.Lti13Platform.WebExample/DevTunnelHttpContextAccessor.cs
index 00916c1..ef64929 100644
--- a/NP.Lti13Platform.WebExample/DevTunnelHttpContextAccessor.cs
+++ b/NP.Lti13Platform.WebExample/DevTunnelHttpContextAccessor.cs
@@ -1,43 +1,42 @@
-namespace NP.Lti13Platform.WebExample
+namespace NP.Lti13Platform.WebExample;
+
+// Copied from HttpContextAccessor (with VS_TUNNEL_URL modification)
+public class DevTunnelHttpContextAccessor : IHttpContextAccessor
{
- // Copied from HttpContextAccessor (with VS_TUNNEL_URL modification)
- public class DevTunnelHttpContextAccessor : IHttpContextAccessor
- {
- private static readonly AsyncLocal _httpContextCurrent = new();
+ private static readonly AsyncLocal _httpContextCurrent = new();
- public HttpContext? HttpContext
+ public HttpContext? HttpContext
+ {
+ get
{
- get
+ return _httpContextCurrent.Value?.Context;
+ }
+ set
+ {
+ var holder = _httpContextCurrent.Value;
+ if (holder != null)
{
- return _httpContextCurrent.Value?.Context;
+ // Clear current HttpContext trapped in the AsyncLocals, as its done.
+ holder.Context = null;
}
- set
+
+ if (value != null)
{
- var holder = _httpContextCurrent.Value;
- if (holder != null)
+ var devTunnel = Environment.GetEnvironmentVariable("VS_TUNNEL_URL");
+ if (!string.IsNullOrWhiteSpace(devTunnel))
{
- // Clear current HttpContext trapped in the AsyncLocals, as its done.
- holder.Context = null;
+ value.Request.Host = new HostString(new Uri(devTunnel).Host);
}
- if (value != null)
- {
- var devTunnel = Environment.GetEnvironmentVariable("VS_TUNNEL_URL");
- if (!string.IsNullOrWhiteSpace(devTunnel))
- {
- value.Request.Host = new HostString(new Uri(devTunnel).Host);
- }
-
- // Use an object indirection to hold the HttpContext in the AsyncLocal,
- // so it can be cleared in all ExecutionContexts when its cleared.
- _httpContextCurrent.Value = new HttpContextHolder { Context = value };
- }
+ // Use an object indirection to hold the HttpContext in the AsyncLocal,
+ // so it can be cleared in all ExecutionContexts when its cleared.
+ _httpContextCurrent.Value = new HttpContextHolder { Context = value };
}
}
+ }
- private sealed class HttpContextHolder
- {
- public HttpContext? Context;
- }
+ private sealed class HttpContextHolder
+ {
+ public HttpContext? Context;
}
}
diff --git a/NP.Lti13Platform.WebExample/Program.cs b/NP.Lti13Platform.WebExample/Program.cs
index 13c6f2d..53b47e3 100644
--- a/NP.Lti13Platform.WebExample/Program.cs
+++ b/NP.Lti13Platform.WebExample/Program.cs
@@ -215,12 +215,12 @@ Task ILti13AssignmentGradeDataService.SaveGradeAsync(Grade grade, CancellationTo
return Task.CompletedTask;
}
- Task ILti13CoreDataService.GetServiceTokenRequestAsync(string toolId, string serviceTokenId, CancellationToken cancellationToken)
+ Task ILti13CoreDataService.GetServiceTokenAsync(string toolId, string serviceTokenId, CancellationToken cancellationToken)
{
return Task.FromResult(ServiceTokens.FirstOrDefault(x => x.ToolId == toolId && x.Id == serviceTokenId));
}
- Task ILti13CoreDataService.SaveServiceTokenRequestAsync(ServiceToken serviceToken, CancellationToken cancellationToken)
+ Task ILti13CoreDataService.SaveServiceTokenAsync(ServiceToken serviceToken, CancellationToken cancellationToken)
{
var existing = ServiceTokens.SingleOrDefault(x => x.ToolId == serviceToken.ToolId && x.Id == serviceToken.Id);
if (existing != null)
@@ -282,8 +282,6 @@ Task ILti13CoreDataService.GetPrivateKeyAsync(CancellationToken can
return Task.FromResult(PartialList<(Membership, User)>.Empty);
}
-
-
Task ILti13DeepLinkingDataService.SaveContentItemAsync(string deploymentId, string? contextId, ContentItem contentItem, CancellationToken cancellationToken)
{
var id = Guid.NewGuid().ToString();
@@ -331,12 +329,12 @@ Task ILti13CoreDataService.GetCustomPermissions(string deploy
public Task GetUserPermissionsAsync(string deploymentId, string userId, CancellationToken cancellationToken = default)
{
- return Task.FromResult(new UserPermissions { FamilyName = true, Name = true, GivenName = true });
+ return Task.FromResult(new UserPermissions { UserId = userId, FamilyName = true, Name = true, GivenName = true });
}
- public Task> GetUserPermissionsAsync(string deploymentId, IEnumerable userIds, CancellationToken cancellationToken = default)
+ public Task> GetUserPermissionsAsync(string deploymentId, IEnumerable userIds, CancellationToken cancellationToken = default)
{
- return Task.FromResult(userIds.Select(x => (x, new UserPermissions { FamilyName = true, Name = true, GivenName = true })));
+ return Task.FromResult(userIds.Select(x => new UserPermissions { UserId = x, FamilyName = true, Name = true, GivenName = true }));
}
}
}
\ No newline at end of file
diff --git a/NP.Lti13Platform/Startup.cs b/NP.Lti13Platform/Startup.cs
index 21c8db4..e2d3231 100644
--- a/NP.Lti13Platform/Startup.cs
+++ b/NP.Lti13Platform/Startup.cs
@@ -9,48 +9,47 @@
using NP.Lti13Platform.NameRoleProvisioningServices;
using NP.Lti13Platform.NameRoleProvisioningServices.Configs;
-namespace NP.Lti13Platform
+namespace NP.Lti13Platform;
+
+public static class Startup
{
- public static class Startup
+ public static Lti13PlatformBuilder AddLti13Platform(this IServiceCollection services)
{
- public static Lti13PlatformBuilder AddLti13Platform(this IServiceCollection services)
- {
- return services
- .AddLti13PlatformCore()
- .AddLti13PlatformDeepLinking()
- .AddLti13PlatformNameRoleProvisioningServices()
- .AddLti13PlatformAssignmentGradeServices();
- }
-
- public static Lti13PlatformBuilder WithLti13DataService(this Lti13PlatformBuilder builder, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
- where T : ILti13DataService
- {
- builder.WithLti13CoreDataService(serviceLifetime)
- .WithLti13DeepLinkingDataService(serviceLifetime)
- .WithLti13NameRoleProvisioningDataService(serviceLifetime)
- .WithLti13AssignmentGradeDataService(serviceLifetime);
-
- return builder;
- }
+ return services
+ .AddLti13PlatformCore()
+ .AddLti13PlatformDeepLinking()
+ .AddLti13PlatformNameRoleProvisioningServices()
+ .AddLti13PlatformAssignmentGradeServices();
+ }
- public static IEndpointRouteBuilder UseLti13Platform(this IEndpointRouteBuilder app, Func? configure = null)
- {
- Lti13PlatformEndpointsConfig config = new();
- config = configure?.Invoke(config) ?? config;
+ public static Lti13PlatformBuilder WithLti13DataService(this Lti13PlatformBuilder builder, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
+ where T : ILti13DataService
+ {
+ builder.WithLti13CoreDataService(serviceLifetime)
+ .WithLti13DeepLinkingDataService(serviceLifetime)
+ .WithLti13NameRoleProvisioningDataService(serviceLifetime)
+ .WithLti13AssignmentGradeDataService(serviceLifetime);
- return app
- .UseLti13PlatformCore(x => config.Core)
- .UseLti13PlatformDeepLinking(x => config.DeepLinking)
- .UseLti13PlatformNameRoleProvisioningServices(x => config.NameRoleProvisioningServices)
- .UseLti13PlatformAssignmentGradeServices(x => config.AssignmentGradeServices);
- }
+ return builder;
}
- public class Lti13PlatformEndpointsConfig
+ public static IEndpointRouteBuilder UseLti13Platform(this IEndpointRouteBuilder app, Func? configure = null)
{
- public Lti13PlatformCoreEndpointsConfig Core { get; set; } = new();
- public DeepLinkingEndpointsConfig DeepLinking { get; set; } = new();
- public EndpointsConfig NameRoleProvisioningServices { get; set; } = new();
- public ServiceEndpointsConfig AssignmentGradeServices { get; set; } = new();
+ Lti13PlatformEndpointsConfig config = new();
+ config = configure?.Invoke(config) ?? config;
+
+ return app
+ .UseLti13PlatformCore(x => config.Core)
+ .UseLti13PlatformDeepLinking(x => config.DeepLinking)
+ .UseLti13PlatformNameRoleProvisioningServices(x => config.NameRoleProvisioningServices)
+ .UseLti13PlatformAssignmentGradeServices(x => config.AssignmentGradeServices);
}
+}
+
+public class Lti13PlatformEndpointsConfig
+{
+ public Lti13PlatformCoreEndpointsConfig Core { get; set; } = new();
+ public DeepLinkingEndpointsConfig DeepLinking { get; set; } = new();
+ public EndpointsConfig NameRoleProvisioningServices { get; set; } = new();
+ public ServiceEndpointsConfig AssignmentGradeServices { get; set; } = new();
}
\ No newline at end of file