|
- <!-- #include file="../aspunit/Lib/ASPUnit.asp" -->
- <!-- #include file="../bootstrap.asp" -->
- <!-- #include file="../../core/lib.json.asp" -->
- <!-- #include file="../../core/lib.Keycloak.asp" -->
-
- <%
- Call ASPUnit.AddModule( _
- ASPUnit.CreateModule( _
- "Keycloak Callback Behavior Tests", _
- Array( _
- ASPUnit.CreateTest("EnsurePendingLoginValueCreatesNewValueWhenMissing"), _
- ASPUnit.CreateTest("EnsurePendingLoginValueReusesExistingValue"), _
- ASPUnit.CreateTest("HandleCallbackReturnsFalseWhenCodeIsMissing"), _
- ASPUnit.CreateTest("HandleCallbackSetsErrorMessageOnMissingCode"), _
- ASPUnit.CreateTest("StateValidationErrorExplainsMissingStoredState"), _
- ASPUnit.CreateTest("StateValidationErrorExplainsMismatchedStoredState"), _
- ASPUnit.CreateTest("IsLoggedInReturnsFalseWithEmptySession"), _
- ASPUnit.CreateTest("IsLoggedInReturnsTrueWhenTokenStoredInSession"), _
- ASPUnit.CreateTest("ClearSessionRemovesStoredTokens") _
- ), _
- ASPUnit.CreateLifeCycle("SetupKeycloakCallbackBehavior", "TeardownKeycloakCallbackBehavior") _
- ) _
- )
-
- Call ASPUnit.Run()
-
- Sub SetupKeycloakCallbackBehavior()
- Call ResetTestRuntime()
- KeycloakAuth_Class__Singleton = Empty
- Session.Contents.Remove "Keycloak_AccessToken"
- Session.Contents.Remove "Keycloak_RefreshToken"
- Session.Contents.Remove "Keycloak_IdToken"
- Session.Contents.Remove "Keycloak_State"
- Session.Contents.Remove "Keycloak_Nonce"
- Session.Contents.Remove "Keycloak_UserInfoJson"
- End Sub
-
- Sub TeardownKeycloakCallbackBehavior()
- Session.Contents.Remove "Keycloak_AccessToken"
- Session.Contents.Remove "Keycloak_RefreshToken"
- Session.Contents.Remove "Keycloak_IdToken"
- Session.Contents.Remove "Keycloak_State"
- Session.Contents.Remove "Keycloak_Nonce"
- Session.Contents.Remove "Keycloak_UserInfoJson"
- KeycloakAuth_Class__Singleton = Empty
- Call ResetTestRuntime()
- End Sub
-
- ' Builds a configured KeycloakAuth_Class instance for callback behavior tests
- Function NewCallbackTestAuth()
- Dim auth
- Set auth = New KeycloakAuth_Class
- Call auth.Configure("https://login.example.test/", "survey", "classic-app", "secret", "https://app.example.test/auth/callback")
- auth.LogoutRedirectUri = "https://app.example.test/"
- Set NewCallbackTestAuth = auth
- End Function
-
- ' A fresh login should create a new pending value in Session so the redirect can
- ' include state/nonce even when the session starts empty.
- Function EnsurePendingLoginValueCreatesNewValueWhenMissing()
- Dim auth, value
- Set auth = NewCallbackTestAuth()
-
- Session.Contents.Remove "Keycloak_State"
- value = auth.EnsurePendingLoginValue("Keycloak_State")
-
- Call ASPUnit.Ok(Len(value) > 0, "EnsurePendingLoginValue should create a value when the session does not have one")
- Call ASPUnit.Equal(Session("Keycloak_State"), value, "EnsurePendingLoginValue should store the created value in Session")
- End Function
-
- ' Repeated hits to /auth/login in the same session should keep using the same
- ' pending state instead of overwriting it and breaking the first callback.
- Function EnsurePendingLoginValueReusesExistingValue()
- Dim auth, value
- Set auth = NewCallbackTestAuth()
-
- Session("Keycloak_State") = "existing-state-123"
- value = auth.EnsurePendingLoginValue("Keycloak_State")
-
- Call ASPUnit.Equal(value, "existing-state-123", "EnsurePendingLoginValue should reuse an existing pending value")
- Call ASPUnit.Equal(Session("Keycloak_State"), "existing-state-123", "EnsurePendingLoginValue should not overwrite an existing session value")
- End Function
-
- ' HandleCallback returns False when the request has no 'code' query parameter
- ' (the typical outcome of a direct navigation or an incomplete redirect)
- Function HandleCallbackReturnsFalseWhenCodeIsMissing()
- Dim auth, result
- Set auth = NewCallbackTestAuth()
- result = auth.HandleCallback()
- Call ASPUnit.Ok(Not CBool(result), "HandleCallback should return False when no authorization code is present in the request")
- End Function
-
- ' HandleCallback must describe the failure in ErrorMessage so the view
- ' can surface a meaningful message to the user
- Function HandleCallbackSetsErrorMessageOnMissingCode()
- Dim auth
- Set auth = NewCallbackTestAuth()
- Call auth.HandleCallback()
- Call ASPUnit.Ok( _
- InStr(LCase(auth.ErrorMessage), "authorization code") > 0, _
- "HandleCallback should set an error message mentioning 'authorization code' when the code parameter is absent" _
- )
- End Function
-
- ' IsLoggedIn reflects the presence of an access token in the Session —
- ' no token means not logged in
- Function IsLoggedInReturnsFalseWithEmptySession()
- Dim auth
- Set auth = NewCallbackTestAuth()
- Call ASPUnit.Ok(Not auth.IsLoggedIn(), "IsLoggedIn should return False when no access token is stored in session")
- End Function
-
- ' Placing a token in the Session should cause IsLoggedIn to return True
- ' without any HTTP call — the session IS the auth state
- Function IsLoggedInReturnsTrueWhenTokenStoredInSession()
- Dim auth
- Set auth = NewCallbackTestAuth()
- Session("Keycloak_AccessToken") = "eyJ.test.token"
- Call ASPUnit.Ok(auth.IsLoggedIn(), "IsLoggedIn should return True when an access token is present in session")
- End Function
-
- ' ClearSession must remove all Keycloak_ prefixed keys so that a subsequent
- ' IsLoggedIn check correctly reports the user as signed out
- Function ClearSessionRemovesStoredTokens()
- Dim auth
- Set auth = NewCallbackTestAuth()
- Session("Keycloak_AccessToken") = "eyJ.test.token"
- Session("Keycloak_RefreshToken") = "eyJ.refresh.token"
- Call auth.ClearSession()
- Call ASPUnit.Ok(Not auth.IsLoggedIn(), "ClearSession should remove stored tokens so IsLoggedIn returns False")
- End Function
-
- ' When the stored login state is absent, the callback failure should point to
- ' session loss, callback reload, or direct navigation instead of a generic
- ' mismatch so troubleshooting is faster.
- Function StateValidationErrorExplainsMissingStoredState()
- Dim auth, message
- Set auth = NewCallbackTestAuth()
-
- message = auth.StateValidationError("callback-state-123")
-
- Call ASPUnit.Ok( _
- InStr(LCase(message), "stored session state is missing") > 0, _
- "StateValidationError should explain when the stored session state is missing" _
- )
- End Function
-
- ' When a session state exists but differs from the callback state, the helper
- ' should describe a stale callback or overlapping login rather than session loss.
- Function StateValidationErrorExplainsMismatchedStoredState()
- Dim auth, message
- Set auth = NewCallbackTestAuth()
- Session("Keycloak_State") = "expected-state-123"
-
- message = auth.StateValidationError("callback-state-456")
-
- Call ASPUnit.Ok( _
- InStr(LCase(message), "did not match the active login session") > 0 And InStr(LCase(message), "another login attempt") > 0, _
- "StateValidationError should explain when the callback state differs from the stored login state" _
- )
- End Function
- %>
|