예제 #1
0
        /// <summary>
        /// OAuth 2.0 validation
        /// </summary>
        /// <returns></returns>
        private JwtSecurityToken ValidateOAuth20()
        {
            // OAuth 2 Specs for Platform Originating Messages
            // https://www.imsglobal.org/spec/security/v1p0/#platform-originating-messages

            // Get the JWT Token from the id_token form field.
            // We could detect the LTI version by checking which OAuth Form Fields are present.
            // Presence of id_token indicates OAuth 2. Once the token is unpacked, we can check the LTI Version Claim for the exact LTI version
            string ltiLaunchJwtToken = _formData["id_token"];

            if (ltiLaunchJwtToken == null)
            {
                return(null);
            }

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

            TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                // OAuth 2.0 Required Validations (https://www.imsglobal.org/spec/security/v1p0/#message-security-and-message-signing)

                // 5.1.3 Authentication Response Validation

                // 5.1.3.1
                IssuerSigningKeys        = OutOfBandData.GetPlatformSigningKeys(),
                ValidateIssuerSigningKey = true,
                RequireSignedTokens      = true,

                // 5.1.3.2
                ValidIssuer    = OutOfBandData.PlatforIssuerId,
                ValidateIssuer = true,

                // 5.1.3.3
                ValidAudience    = OutOfBandData.ToolClientId,
                ValidateAudience = true,

                // 5.1.3.4, 5.1.3.5 related to multiple audiences - skip for now

                // 5.1.3.7, 5.1.3.8
                ValidateLifetime      = true,
                RequireExpirationTime = true,

                // 5.1.3.9
                TokenReplayCache    = _replayCache,
                ValidateTokenReplay = true,

                ClockSkew = new TimeSpan(0, 0, 15)
            };

            // Validate token.
            ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(ltiLaunchJwtToken, validationParameters, out SecurityToken validatedToken);

            // Set the ClaimsPrincipal on the current thread.
            Thread.CurrentPrincipal = claimsPrincipal;

            // Set the ClaimsPrincipal on HttpContext.Current if the app is running in web hosted environment.
            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = claimsPrincipal;
            }

            // If the token is scoped, verify that required permission is set in the scope claim.
            // See https://www.imsglobal.org/spec/security/v1p0/#access-token-management
            // 7.1 Access Token Management
            if (ClaimsPrincipal.Current.FindFirst(scopeClaimType) != null && ClaimsPrincipal.Current.FindFirst(scopeClaimType).Value != "user_impersonation")
            {
                return(null);
            }

            // now we have validated token and can use the claims containing usable LTI Launch parameters
            var launchToken = (JwtSecurityToken)validatedToken;

            return(launchToken);
        }
예제 #2
0
        protected void Page_Load(object sender, EventArgs e)
        {
            // Initial page load - redirect to the IMS reference platform (acting as the LMS) set up on the IMS website
            if (!Request.HttpMethod.Equals("POST", StringComparison.InvariantCultureIgnoreCase))
            {
                Response.Redirect("https://lti-ri.imsglobal.org/platforms/44/resource_links", true);
                return;
            }



            // OAuth 2 Specs for Platform Originating Messages
            // https://www.imsglobal.org/spec/security/v1p0/#platform-originating-messages

            // Get the JWT Token from the id_token form field.
            // We could detect the LTI version by checking which OAuth Form Fields are present.
            // Presence of id_token indicates OAuth 2. Once the token is unpacked, we can check the LTI Version Claim for the exact LTI version
            string ltiLaunchJwtToken = Request.Form["id_token"];

            if (ltiLaunchJwtToken == null)
            {
                CompleteRequest(HttpStatusCode.Forbidden, "JWT token was not provided in id_token form field");
                return;
            }

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

            TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                // OAuth 2.0 Required Validations (https://www.imsglobal.org/spec/security/v1p0/#message-security-and-message-signing)

                // 5.1.3 Authentication Response Validation

                // 5.1.3.1
                IssuerSigningKeys        = OutOfBandData.GetPlatformSigningKeys(),
                ValidateIssuerSigningKey = true,
                RequireSignedTokens      = true,

                // 5.1.3.2
                ValidIssuer    = OutOfBandData.PlatforIssuerId,
                ValidateIssuer = true,

                // 5.1.3.3
                ValidAudience    = OutOfBandData.ToolClientId,
                ValidateAudience = true,

                // 5.1.3.4, 5.1.3.5 related to multiple audiences - skip for now

                // 5.1.3.7, 5.1.3.8
                ValidateLifetime      = true,
                RequireExpirationTime = true,

                // 5.1.3.9
                TokenReplayCache    = _replayCache,
                ValidateTokenReplay = true,

                ClockSkew = new TimeSpan(0, 0, 15)
            };

            try
            {
                // Validate token.
                ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(ltiLaunchJwtToken, validationParameters, out SecurityToken validatedToken);

                // Set the ClaimsPrincipal on the current thread.
                Thread.CurrentPrincipal = claimsPrincipal;

                // Set the ClaimsPrincipal on HttpContext.Current if the app is running in web hosted environment.
                if (HttpContext.Current != null)
                {
                    HttpContext.Current.User = claimsPrincipal;
                }

                // If the token is scoped, verify that required permission is set in the scope claim.
                // See https://www.imsglobal.org/spec/security/v1p0/#access-token-management
                // 7.1 Access Token Management
                if (ClaimsPrincipal.Current.FindFirst(scopeClaimType) != null && ClaimsPrincipal.Current.FindFirst(scopeClaimType).Value != "user_impersonation")
                {
                    CompleteRequest(HttpStatusCode.Forbidden, "Invalid scope");
                    return;
                }

                // now we have validated token and can use the claims containing usable LTI Launch parameters
                var launchToken = (JwtSecurityToken)validatedToken;

                // Could validate schema here...

                // LTI 1.3 Spec: https://www.imsglobal.org/spec/lti/v1p3
                // COULD USE EXTENSION POINT ON ValidationParameters PERHAPS

                // 2.1.3 lti_deployment_id must be present
                if (!launchToken.Payload.ContainsKey("https://purl.imsglobal.org/spec/lti/claim/deployment_id"))
                {
                    CompleteRequest(HttpStatusCode.Unauthorized, "Deployment id missing");
                    return;
                }

                // 4.3 Required Message Claims

                // 4.3.1 Message type claim
                string messageType = launchToken.Payload.ContainsKey("https://purl.imsglobal.org/spec/lti/claim/message_type") ?
                                     launchToken.Payload["https://purl.imsglobal.org/spec/lti/claim/message_type"].ToString() : "";
                if (messageType != "LtiResourceLinkRequest")
                {
                    CompleteRequest(HttpStatusCode.BadRequest, "message_type claim missing or invalid");
                    return;
                }

                // there are additional validations...

                OutputTokenInfo(launchToken.Payload);
            }
            catch (SecurityTokenValidationException stve)
            {
                CompleteRequest(HttpStatusCode.Unauthorized, stve.Message);
                return;
            }
            catch (Exception ex2)
            {
                CompleteRequest(HttpStatusCode.InternalServerError, ex2.Message);
                return;
            }

            CompleteRequest(HttpStatusCode.OK, "");
        }