Registering a NUMBAS-LTI server with Anthology

66 views
Skip to first unread message

David Martin

unread,
Sep 25, 2025, 3:48:54 AMSep 25
to Numbas Users
I have just installed NUMBAS-LTI via docker on RHEL9 and it seems to run fine. My next step is to register it with Anthology so I can add it to our blackboard instance.

Couple of things: 
1. The link to anthology in the server is now broken. You have to find the right page at developer.anthology.com.
2. When I attempt to register I presume I should select the 'My Integration supports LTI 1.3' but then I get requests for a bunch of information I can't find reference to in the docs.

Login initiation URL
Tool Redirect URL(s)
Tool JWKS URL
The default SIgning Algorithm is RS256
Are there any custom parameters to set?

Any help would be appreciated

..d

Dr David Martin
 

Johan Slabbert

unread,
Sep 25, 2025, 4:03:38 AMSep 25
to numbas...@googlegroups.com
Good day

I believe it is a 3 part process



c) In your Blackboard Instance you then : Register LTI 1.3/Advantage Tool where you add the ClientID you created in b:

image.png



Johan Slabbert
Educational Technology Project Manager
BEng (Pret), BEng Hons (Pret), MIT (Pret), MEng(Pret)

Tel +27 (0)12 420 3825
Fax +27 (0)12 420 3697
Email johan.s...@up.ac.za
www.up.ac.za

Department for Education Innovation 
Room 3-34, IT Building, Hatfield Campus
University of Pretoria, Private Bag X20
Hatfield 0028, South Africa




--
You received this message because you are subscribed to the Google Groups "Numbas Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to numbas-users...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/numbas-users/91bcefd6-0c22-4194-b627-6fd695fe5b2cn%40googlegroups.com.

This message and attachments are subject to a disclaimer.
Please refer to http://upnet.up.ac.za/services/it/documentation/docs/004167.pdf 
for full details.

David Martin

unread,
Sep 25, 2025, 4:11:01 AMSep 25
to numbas...@googlegroups.com
The instructions are incomplete as per my email. 

..d

You received this message because you are subscribed to a topic in the Google Groups "Numbas Users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/numbas-users/VGjF5zCA7v0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to numbas-users...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/numbas-users/CABP1u53JqsesTuzrZPBiFBwn5iukoa60LmKKgm3ZAHVNrJwE3Q%40mail.gmail.com.

David Martin

unread,
Sep 25, 2025, 7:10:17 AMSep 25
to numbas...@googlegroups.com
OK, it looks like the broken link mislead me. 

Connect to Blackboard

Blackboard expects most LTI tools to run on a single domain, which serves every institution using it.

Because each institution runs their own instance of the Numbas LTI tool, each instance must be registered separately with Anthology.

  1. Register this server as an application with Anthology.
  2. Create a dynamic registration token.

    In the Anthology Developer portal, use Dynamic Registration with the token URL you created, to initiate tool registration.

    Record the Application ID and the Application Key that were created.


I was under the impression that I had to first register with anthology (step 1), then do the next steps as the page linked was 404ing.

Step 2 I then read as a title  and a single step on anthology where it is in fact multiple steps. a) create a dynamic registration token (on th elocal LTI server) and then step 2b is on the anthology site. 
The application key is not obvious, you have to select the 'manage keys' option fromt eh trhee dots next to the application registration.

So far so good - need to wait on my IT colleagues for the next step.

Best wishes

..d

David Martin

unread,
Nov 3, 2025, 10:38:06 AMNov 3
to Numbas Users
I get a 500 server error when trying to register the app at Anthology. When I add the app in Anthology it asks me for the dynamic registration token. This gives a popup asking if I wish to register. I click register and this prompts a 500 server error. Is there any hint as to how to check the logs to find the error? I am using Docker on RHEL 9.

..d

Christian Lawson-Perfect

unread,
Nov 4, 2025, 7:47:48 AMNov 4
to numbas...@googlegroups.com
Hi David,
Could you try running `docker container logs` on the daphne container? 

David Martin

unread,
Nov 4, 2025, 9:24:32 AMNov 4
to numbas...@googlegroups.com
django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: 'numbas-sls-wp1.dundee.ac.uk'. You may need to add 'numbas-sls-wp1.dundee.ac.uk' to ALLOWED_HOSTS.
Internal Server Error: /
Invalid HTTP_HOST header: 'numbas-sls-wp1.dundee.ac.uk'. You may need to add 'numbas-sls-wp1.dundee.ac.uk' to ALLOWED_HOSTS.
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/utils/deprecation.py", line 128, in __call__
    response = self.process_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/middleware/common.py", line 48, in process_request
    host = request.get_host()
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/http/request.py", line 151, in get_host
    raise DisallowedHost(msg)
django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: 'numbas-sls-wp1.dundee.ac.uk'. You may need to add 'numbas-sls-wp1.dundee.ac.uk' to ALLOWED_HOSTS.
Internal Server Error: /owa/
Invalid HTTP_HOST header: 'numbas-sls-wp1.dundee.ac.uk'. You may need to add 'numbas-sls-wp1.dundee.ac.uk' to ALLOWED_HOSTS.
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/utils/deprecation.py", line 128, in __call__
    response = self.process_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/middleware/common.py", line 48, in process_request
    host = request.get_host()
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/http/request.py", line 151, in get_host
    raise DisallowedHost(msg)
django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: 'numbas-sls-wp1.dundee.ac.uk'. You may need to add 'numbas-sls-wp1.dundee.ac.uk' to ALLOWED_HOSTS.
Internal Server Error: /
Invalid HTTP_HOST header: '172.18.0.5:8700'. You may need to add '172.18.0.5' to ALLOWED_HOSTS.
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/utils/deprecation.py", line 128, in __call__
    response = self.process_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/middleware/common.py", line 48, in process_request
    host = request.get_host()
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/http/request.py", line 151, in get_host
    raise DisallowedHost(msg)
django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: '172.18.0.5:8700'. You may need to add '172.18.0.5' to ALLOWED_HOSTS.
Internal Server Error: /robots.txt

Looking at this it seems that the config requires the internal hostname for the VM as well as the outwardly facing FQDN. I'll try adding env('HOSTNAME') to that in settings.py and restarting the  container

..d

David Martin

unread,
Nov 4, 2025, 9:32:28 AMNov 4
to numbas...@googlegroups.com
That now gives me a new error when trying Dynamic configuration

Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/asgiref/sync.py", line 489, in thread_handler
    raise exc_info[1]
  File "/usr/local/lib/python3.12/site-packages/django/core/handlers/exception.py", line 42, in inner
    response = await get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/asgiref/sync.py", line 489, in thread_handler
    raise exc_info[1]
  File "/usr/local/lib/python3.12/site-packages/django/core/handlers/base.py", line 253, in _get_response_async
    response = await wrapped_callback(
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/asgiref/sync.py", line 439, in __call__
    ret = await asyncio.shield(exec_coro)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/asgiref/current_thread_executor.py", line 40, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/asgiref/sync.py", line 493, in thread_handler
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/views/generic/base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/utils/decorators.py", line 48, in _wrapper
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
    return view_func(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/numbas-lti-provider/numbas_lti/views/lti_13.py", line 391, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/views/generic/base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/django/views/generic/edit.py", line 258, in post
    return self.form_valid(form)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/numbas-lti-provider/numbas_lti/views/lti_13.py", line 426, in form_valid
    lti_tool = registration.register()
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pylti1p3/dynamic_registration.py", line 283, in register
    assert tool_spec in openid_registration, \
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: The OpenID registration is not an LTI tool configuration

David Martin

unread,
Nov 6, 2025, 4:33:00 AMNov 6
to numbas...@googlegroups.com
Tried a couple of things. One was to open up allowed hosts with my local domain as 134.36.0.0/16

Then making sure I had actually completed the App ID/client ID registration

I then get the same nonce error 

Headers from Daphne

172.18.0.5:47662 - - [06/Nov/2025:09:05:56] "GET /consumers/1" 200 3817
172.18.0.5:34872 - - [06/Nov/2025:09:06:05] "GET /lti13/login/?iss=https%3A%2F%2Fblackboard.com&login_hint=https%253A%252F%252Fdundee-staging.blackboard.com%252Fwebapps%252Fblackboard%252Fexecute%252Fblti%252FlaunchPlacement%253Fcmd%253Dauthenticate%2526course_id%253D_81212_1%2Cc248f60be70545a2a2259f1039aab68f%2Cd2613617ee75460cad98300c853d7341&target_link_uri=https%3A%2F%2Fnumbas.dundee.ac.uk%2Flti13%2Flaunch%2F&lti_message_hint=eyJjdXN0b21QYXJhbXMiOnt9LCJvcGVuSW5MaWdodEJveCI6ZmFsc2UsImZyb21VbHRyYSI6dHJ1ZSwiaW5saW5lTW9kZSI6ZmFsc2UsImhpZGVEb2N1bWVudCI6ZmFsc2UsInBsYWNlbWVudElkIjoiXzEzMTFfMSIsInRhcmdldE92ZXJyaWRlIjpudWxsLCJmcm9tR3JhZGVDZW50ZXIiOmZhbHNlLCJvcGVuTmV3V2luZG93IjpmYWxzZSwiZGVlcExpbmtMYXVuY2giOnRydWUsInBhcmVudENvbnRlbnRJZCI6IiIsInJlc291cmNlTGlua0lkIjoiXzc1NTExMzFfMSIsImNvdXJzZUlkIjoiXzgxMjEyXzEiLCJncm91cElkIjpudWxsLCJjb250ZW50SWQiOiJfNzU1MTEzMV8xIiwiZGVwbG95bWVudElkIjoiNDhiOTA1YzAtYjdiYS00NmI1LWFlM2EtY2FjYzJkZDQwNDI2IiwicG9zaXRpb24iOjUsInRhcmdldExpbmtVcmwiOiJodHRwczovL251bWJhcy5kdW5kZWUuYWMudWsvbHRpMTMvbGF1bmNoLyJ9&lti_deployment_id=48b905c0-b7ba-46b5-ae3a-cacc2dd40426&client_id=ab972195-c99f-4e84-ae76-bc344d14cd2a&lti_storage_target=lti_storage_frame" 200 4229
172.18.0.5:34880 - - [06/Nov/2025:09:06:05] "GET /lti13/login/?iss=https%3A%2F%2Fblackboard.com&login_hint=https%253A%252F%252Fdundee-staging.blackboard.com%252Fwebapps%252Fblackboard%252Fexecute%252Fblti%252FlaunchPlacement%253Fcmd%253Dauthenticate%2526course_id%253D_81212_1%2Cc248f60be70545a2a2259f1039aab68f%2Cd2613617ee75460cad98300c853d7341&target_link_uri=https%3A%2F%2Fnumbas.dundee.ac.uk%2Flti13%2Flaunch%2F&lti_message_hint=eyJjdXN0b21QYXJhbXMiOnt9LCJvcGVuSW5MaWdodEJveCI6ZmFsc2UsImZyb21VbHRyYSI6dHJ1ZSwiaW5saW5lTW9kZSI6ZmFsc2UsImhpZGVEb2N1bWVudCI6ZmFsc2UsInBsYWNlbWVudElkIjoiXzEzMTFfMSIsInRhcmdldE92ZXJyaWRlIjpudWxsLCJmcm9tR3JhZGVDZW50ZXIiOmZhbHNlLCJvcGVuTmV3V2luZG93IjpmYWxzZSwiZGVlcExpbmtMYXVuY2giOnRydWUsInBhcmVudENvbnRlbnRJZCI6IiIsInJlc291cmNlTGlua0lkIjoiXzc1NTExMzFfMSIsImNvdXJzZUlkIjoiXzgxMjEyXzEiLCJncm91cElkIjpudWxsLCJjb250ZW50SWQiOiJfNzU1MTEzMV8xIiwiZGVwbG95bWVudElkIjoiNDhiOTA1YzAtYjdiYS00NmI1LWFlM2EtY2FjYzJkZDQwNDI2IiwicG9zaXRpb24iOjUsInRhcmdldExpbmtVcmwiOiJodHRwczovL251bWJhcy5kdW5kZWUuYWMudWsvbHRpMTMvbGF1bmNoLyJ9&lti_deployment_id=48b905c0-b7ba-46b5-ae3a-cacc2dd40426&client_id=ab972195-c99f-4e84-ae76-bc344d14cd2a&lti_storage_target=lti_storage_frame&lti1p3_new_window=1" 302 -

Richard Marshall

unread,
Dec 9, 2025, 4:34:55 AM (5 days ago) Dec 9
to Numbas Users

Hi.

I am having the same issue as David, while trying to create a Dynamic Registration LTI 1.3:  I get a 500 server error when trying to register the app at Anthology. When I add the app in Anthology it asks me for the dynamic registration token. This gives a popup asking if I wish to register. I click register and this prompts a 500 server error

Within the Daphne logs I get AssertionError: The OpenID registration is not an LTI tool configuration.  I also get a lot of ALLOWED_HOSTS errors, which follow the same pattern; the errors are for an IP address rather than domain name, and that IP address happens to be the container IP, calling itself.  We have the same issue on our pilot instance and hasn't seemed to be a problem for the past year.)

<code bash>
==============================================================                                                   
Container ID   : 5a1ff5ec1712                                                                                    
Container Name : numbas-lti-provider-docker-daphne-1                                                             
                                                                                                                 
Logs:

    raise DisallowedHost(msg)
django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: '192.168.2.40:8700'. You may need to add '192.16
8.2.40' to ALLOWED_HOSTS.
Internal Server Error: /
Internal Server Error: /lti13/register/dynamic/6a50c827-8f45-40b1-a71e-ceaa53bb8bf2/use/

Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/asgiref/sync.py", line 555, in thread_handler
--
  File "/usr/local/lib/python3.12/site-packages/pylti1p3/dynamic_registration.py", line 290, in register
    tool_spec in openid_registration

AssertionError: The OpenID registration is not an LTI tool configuration
Invalid HTTP_HOST header: '192.168.2.40:8700'. You may need to add '192.168.2.40' to ALLOWED_HOSTS.</code>

Richard Marshall

unread,
Dec 9, 2025, 7:55:35 AM (4 days ago) Dec 9
to Numbas Users
AssertionError: The OpenID registration is not an LTI tool configuration
This error message typically occurs when a platform or learning management system (LMS) is expecting a standard LTI (Learning Tools Interoperability) configuration but receives a request intended for an OpenID Connect (OIDC) registration process instead.
LTI and OIDC are two different standards used for integrating external tools into an LMS: 
  • LTI (versions 1.1, 1.2, 1.3): The traditional standard for tool integration. The LMS usually finds the configuration details (like launch URLs and keys) via an XML configuration file or a manual entry form.
  • OpenID Connect (OIDC) / LTI Advantage: The newer, more secure standard. The registration process involves a dynamic handshake where the LMS automatically registers its details with the external tool provider. 
The AssertionError means the system's code is strictly enforcing that the incoming request must be an LTI configuration request, and it failed to assert that the OpenID registration request met that specific condition.

Richard Marshall

unread,
Dec 9, 2025, 10:59:24 AM (4 days ago) Dec 9
to Numbas Users
As an experiment I logged on to our 'working' Pilot server (one year old) and created a new LTI 1.3 token for Blackboard.  I then used this in Blackboard, and also received a Server 500 error.

Christian Lawson-Perfect

unread,
Dec 9, 2025, 11:13:09 AM (4 days ago) Dec 9
to numbas...@googlegroups.com
It seems that Blackboard have changed their registration process. I don't have access to a a Blackboard environment to test against, so I think I'll need the help of someone who does have a Blackboard environment to work this one out.

Richard Marshall

unread,
Dec 10, 2025, 6:38:15 AM (3 days ago) Dec 10
to Numbas Users
Hi Christian

I am available to test this morning, or Thursday, Friday

Richard
Reply all
Reply to author
Forward
0 new messages