{"id":47594574,"url":"https://github.com/oxctl/tool-support","last_synced_at":"2026-04-01T17:57:25.070Z","repository":{"id":346545702,"uuid":"244970765","full_name":"oxctl/tool-support","owner":"oxctl","description":"A LTI Launch server and JWT authenticated proxy server. Also does OAuth2 token management to Canvas.","archived":false,"fork":false,"pushed_at":"2026-03-24T10:01:14.000Z","size":1015,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-03-25T12:43:04.716Z","etag":null,"topics":["tool"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oxctl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-03-04T18:09:45.000Z","updated_at":"2026-03-18T16:00:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/oxctl/tool-support","commit_stats":null,"previous_names":["oxctl/tool-support"],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/oxctl/tool-support","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxctl%2Ftool-support","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxctl%2Ftool-support/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxctl%2Ftool-support/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxctl%2Ftool-support/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxctl","download_url":"https://codeload.github.com/oxctl/tool-support/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxctl%2Ftool-support/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290715,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["tool"],"created_at":"2026-04-01T17:57:24.444Z","updated_at":"2026-04-01T17:57:25.062Z","avatar_url":"https://github.com/oxctl.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Canvas Tool Support\n\n[![Java CI with Maven](https://github.com/oxctl/canvas-proxy/actions/workflows/build.yml/badge.svg)](https://github.com/oxctl/canvas-proxy/actions/workflows/build.yml)\n\n## Overview\n\nThis is a webapp that supports LTI tools in Canvas. It handles the LTI 1.3 launch and allows tools to retrieve the JWT that is created through the LTI launch. It also allows the management of OAuth2 tokens for accessing Canvas and links them to a LTI tool. This allows a HTML/JS (React) page to get data about the LTI launch and then make `fetch()` requests to Canvas through this tool. This allows a completely static tool that still provides useful functionality. The webapp supports multiple LTI tool at the same time.\n\n\n## Getting Going\n\nIf you just want to run the application and don't need to develop it then you can use the docker setup as outlined in [docs/deploy.md](docs/deploy.md).\n\n## Generating a key \n\nWhen the service starts up if there isn't a keypair available for signing it generates one. This keypair isn't persisted, so it will be re-generated the next time the service starts. If you are using NRPS or Deep Linking then Canvas will use the keypair for verifying messages. The log message when a keypair is generated is:\n```\nuk.ac.ox.ctl.ltiauth.Lti13Configuration  : Generated a keypair, this shouldn't be used in production.\n```\n\nTo generate a static keypair you can use the `keytool` command:\na\n```bash\nkeytool \\\n  -genkeypair \\\n  -alias jwt \\\n  -keyalg RSA \\\n  -keystore config/jwk.jks \\\n  -storepass store-pass \\\n  -dname CN=development\n```\n\n## History\n\nOriginally this project was 2 projects, one handling the LTI launch, and one handling the OAuth2 proxying to Canvas. This made configuring a new setup more complex (2 services changed) and also made hosting more expensive. When we were looking at allowing configuration to be edited dynamically we decided that the best solution would be to merge the two projects into one and then the configuration can be edited in just one place.\n\n## Design\n\n### LTI Tool Launch\n\nThis is when a user in a service like Canvas click on the link to launch an LTI tool (or it may be in an iframe so launched when the page loads). The launch is designed to pass across information about who the current user is and where in the LMS/VLE they are in a secure way so that the integrated functionality can present them with some functionality.\n\nThe easiest way to understand what's happening is with a sequence diagram:\n\n```mermaid\nsequenceDiagram\n    autonumber\n    participant Browser\n    participant Tool Support\n    participant Canvas\n    participant LTI Tool\n    rect rgb(240,240,240)\n    Note over Tool Support: Start LTI 1.3 Standard\n    Browser-\u003e\u003eTool Support: LTI initiation request (POST /lti/login_initation/{registrationId})\n    Tool Support-\u003e\u003eBrowser: 302\n    Browser-\u003e\u003eCanvas: Authorization Redirect (GET https://sso.canvaslms.com/api/lti/authorize_redirect)\n    Canvas-\u003e\u003eBrowser: 302\n    Browser-\u003e\u003eCanvas: Authorization (GET https://instance.instructure.com/api/lti/authorize)\n    Canvas-\u003e\u003eBrowser: 200 (HTML autosubmit)\n    Browser-\u003e\u003eTool Support: LTI Login (POST /lti/login)\n    Note over Tool Support: End LTI 1.3 Standard\n    end\n    Tool Support-\u003e\u003eBrowser: 302\n    Note over Browser: Redirect is used here\u003cbr\u003eto keep the service\u003cbr\u003egeneric.\n    Browser-\u003e\u003eLTI Tool: Show Tool (GET https://lti.example/?id=1234)\n    Note over LTI Tool: This can be served\u003cbr\u003e from a static website\n    LTI Tool-\u003e\u003eBrowser: 200 (HTML for tool)\n    Browser--\u003e\u003eTool Support: GET token (fetch POST /token id=1234)\n    Tool Support--\u003e\u003eBrowser: 200 (JSON containing JWT)\n    alt\n    Tool Support--\u003e\u003eBrowser: 404 (No token found for ID, eg already retrieved)\n    end\n    alt\n    Tool Support--\u003e\u003eBrowser: 403 (When user is not allowed to use tool)\n    end\n```\n\n#### Audience Validation\n\nAs there isn't a secret that is needed to configure the tool we must do audience validation so that only allowed services can perform a LTI launch. Generally the final endpoint that gets a JWT as authentication should be doing the audience validation, but it may be helpful to also allow it to be done at the point of the LTI launch.\n\n### LTI Names and Roles Provisioning Service\n\nThis service supports clients using the JWT created as part of the LTI authentication flow to make Names and Roles Provisioning Service (NRPS) requests to `/nrps/` supplying the JWT as a Bearer in the Authorization header. If the tool has the NRPS service enable then a request will be made to the NRPS endpoint and then if there are any further pages of responses these will be retrieved as well and returned to client in a single response.\n\nThe ability to use this endpoint is controlled by configuration which specifies which roles are allowed to use the NRPS. This is because typically you don't want student roles to be able to use this endpoint as it supplies details that may be hidden from them (eg email address). Multiple roles can be specified separated by commas.\n\nThe service allows applications to self-sign JWTs making requests to the nrps endpoint. The token for the request must be signed with an HMAC secret which matches that in `uk.ac.ox.ctl.model.Tool.secret`, and must have the same issuer as `uk.ac.ox.ctl.model.Tool.issuer`.\n\n#### Generating a secret\n\nThe secret needs to be at least 256 bits long and should be base64 encoded in the JSON when updating a tool. The simplest way to generate a secret is with:\n\n```bash\nhead -c 32 \u003c /dev/urandom | base64\n```\n\n### LTI Deep Linking\n\nThere is support in the LTI Server to sign deep linking responses before they get sent back to Canvas. This is so that static tools (without a server side component) can sign a JWT for the deep linking response. The specification for this is at: https://www.imsglobal.org/spec/lti-dl/v2p0\n\nThe launch flow should be very similar to a normal LTI launch, however once the tool has enough information to send the data back to Canvas it can use the deep linking endpoint to get a signed JWT:\n\n```mermaid\nsequenceDiagram\n    autonumber\n    Canvas-\u003e\u003eTool Support: LTI Launch (JWT)\n    Tool Support-\u003e\u003e+Tool: Token ID\n    Tool-\u003e\u003e+Tool Support: Token ID (POST /token)\n    Tool Support -\u003e\u003e-Tool: JWT\n    Note over Tool: User uses the tool.\n    Tool-\u003e\u003e+Tool Support: Content Items (POST /deep-linking)\n    Tool Support-\u003e\u003e-Tool:  Signed JWT\n    Tool-\u003e\u003eCanvas: Signed JWT (to return URL from JWT)\n```\n\nThe deep linking endpoint takes the JSON that would normally be included in the content items section of the JWT.\n\n### Example usage\n\nThis is how it is implemented in calendar import:\n\n```js\nconst url = this.props.ltiServer + '/deep-linking'\n\nconst body = {\n  \"https://purl.imsglobal.org/spec/lti-dl/claim/content_items\": [{\n    \"type\": \"ltiResourceLink\",\n    \"title\": this.state.pageName,\n    \"url\": this.props.targetLinkUri,\n    \"custom\": {\n      \"url\": this.state.url\n    }\n  }]\n}\n\nfetch(\n  url,\n{\n    method: 'POST',\n    body: JSON.stringify(body),\n    headers: new Headers({\n      'Content-Type': 'application/json',\n      'Authorization': 'Bearer ' + this.props.token\n    })\n  }\n).then((response) =\u003e {\n  if (!response.ok) {\n    throw Error(\"\" + response.status);\n  }\n  return response.json()\n}).then((json) =\u003e {\n  this.setState({deepLinkingJwt: json['jwt']})\n  document.getElementById('deepLinkingForm').submit();\n})\n```\n\nA fetch POST is made to the deep-linking endpoint  e.g. 'https://lti.canvas.ox.ac.uk/deep-linking'. The body is a JSON object comprised of the content_items claim with a type of \"ltiResourceLink\". The url is the claim returned from target_link_uri, and any additional contents are in the custom parameter.\nThis returns a jwt which is then auto-submitted to the url from https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings.deep_link_return_url in a form\n\n```js\n\u003cform name=\"deepLinkingForm\" id=\"deepLinkingForm\" method=\"post\" action={this.props.deepLinkReturnUrl}\u003e\n  \u003cinput type=\"hidden\" name=\"JWT\" value={this.state.deepLinkingJwt} /\u003e\n\u003c/form\u003e\n```\n\n\n### Proxy Use\n\nThe proxy allows a tool to make requests to Canvas (eg please list all the courses the current user has) using the browser fetch API, if it doesn't have a token for the user then it returns an error and the tool at this point can prompt the user to start the OAuth2 flow to grant access. Requests to the proxy normally include the JWT from the LTI launch and this is used to work out which developer key should be used (each LTI can optionally have a API key linked to it).\n\n```mermaid\nsequenceDiagram\n    Browser-\u003e\u003e+Tool Support:  GET /api/v1/users/self\u003cbr\u003e(JWT from LTI launch)\n    Note over Tool Support: Lookup user token for Canvas\n    Tool Support-\u003e\u003e+Canvas: GET /api/v1/users/self\u003cbr\u003e(Token loaded from DB)\n    Canvas--\u003e\u003e-Tool Support: user JSON\n    Tool Support--\u003e\u003e-Browser: user JSON\n```\n\n#### Renewing a token\n\nThe endpoint (badly named) to renew a token for a user is `POST /tokens/check`, this attempts to remove the existing token for a user and then gets them to re-grant access to the application. The sequence is as follows:\n\n```mermaid\nsequenceDiagram\n    autonumber\n    Browser-\u003e\u003e+Tool Support: POST https://proxy/tokens/check\n    note right of Browser: This includes the JWT as a request parameter which is used to authenticate the request.\n    Tool Support--\u003e\u003e-Browser: 302 https://canvas/login/oauth2/auth....\n    note over Browser,Tool Support: Cookie based session established with Proxy\n    rect rgb(240,240,240)\n    note over Tool Support: Standard OAuth2 Flow to Canvas\n    Browser-\u003e\u003e+Canvas: GET https://canvas/login/oauth2/auth....\n    Canvas--\u003e\u003e-Browser: 302 https://canvas/login/oauth2/confirm\n    Browser-\u003e\u003e+Canvas: GET https://canvas/login/oauth2/confirm\n    Canvas-\u003e\u003e-Browser: 200 HTML ... Confirm/Reject\n    Browser-\u003e\u003e+Canvas: POST https://canvas/login/oauth2/accept\n    Canvas--\u003e\u003e-Browser: 302 https://canvas/login/oauth2/code....\n    Browser-\u003e\u003e+Canvas: GET https://canvas/login/oauth2/code....\n    Canvas--\u003e\u003e-Browser: 302 https://proxy/tokens/check\n    end\n    Browser-\u003e\u003e+Tool Support: GET https://proxy/tokens/check\n    Tool Support-\u003e\u003e+Canvas: GET https://canvas/login/oauth2/token\n    Canvas--\u003e\u003e-Tool Support: 200 Refresh token\n    Tool Support-\u003e\u003e-Browser: 200 HTML ... Access Granted\n\n```\n\n#### Server to Server\n\nWe also support serverside services having a shared secret key to sign requests (with HMAC) and this allows the services to perform requests on behalf of a user once the JWT from the LTI launch has expired. As there is no user present for these requests if the token doesn't exist for the user there is no way to prompt them to grant access again.\n\n## Deployment Configuration\n\n### AWS Elastic Beanstalk\n\nThis application needs a database to store the configuration and the OAuth tokens it gets granted. We have enabled swap on the instance so for test instances we can use low memory machines and still be able to deploy new version without running out of memory.\n\n#### Environmental Variables\n\n- HOSTNAME - The hostname that the server is running on, used to get TLS/config files.\n- LTI_ISSUER - The issuer of JWT tokens that we sign.\n- RDS_HOSTNAME - The MySQL hostname to connect to for the database.\n- RDS_PORT - The MySQL port to use in the connection to the database.\n- RDS_DB_NAME - The name of the MySQL database to use. \n- SECRET_RDS - The name of the secret to get the RDS connection from.\n- SENTRY_DSN - The secret for reporting errors and performance to https://sentry.io\n- SENTRY_ENVIRONMENT - The environment key that is sent to https://sentry.io\n- SPRING_PROFILES_ACTIVE=client,aws - The profiles that are active, this is used to load configuration.\n- SPRING_SECURITY_USER_PASSWORD - The password used for basic authentication to the /admin API.\n\n#### Client Configuration\n\nClient configuration is held in the DB and details of how to update this will be added shortly.\n\n## Notes\n\nAs well as setting the main secret, the JWKs secret needs to be set.  An example command to do this is this:\n\n    aws secretsmanager update-secret --secret-id tool-support/beta/eb-env/config-jks-file --secret-binary fileb:///Users/adamm/Downloads/tools-dev.canvas.ox.ac.uk.jks\n\n### Proxy Error Handling\n\nIf the user removes their token then we get a 401 back from Canvas with a body of:\n{\"errors\":[{\"message\":\"Invalid access token.\"}]}\nWe need to be careful with this as when our JWT is invalid we will also get 401, one difference is that the error proxied from canvas is the header:\n\n    WWW-Authenticate: Bearer realm=\"canvas-lms\"\n\nbut when it's from a missing JWT it's:\n\n     WWW-Authenticate: Bearer realm=\"proxy\"\n    \nand if it's an invalid JWT it's something along the lines of:\n\n    WWW-Authenticate: Bearer error=\"invalid_token\", error_description=\"An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found\", error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"\n\n### Signing JWTs\n\nFor each LTI key you can select if the JWT that is handed to the tool is the original one signed by the VLE (Canvas) or if we should sign the JWT ourselves. One reason for signing ourselves is that we can then make the expiry on the JWT longer, but in the future it could also allow us to add extra data or to strip out some data.\n\nThe public key that the JWTs are signed using is available on `/.well-known/jwks.json`, so any service consuming these JWTs will need to verify them against that key.\n\n### LTI Custom Fields \n\nWe have special support for the following custom fields:\n\n- `allowed_roles` - When set on the LTI launch this should be a comma separated list of Canvas roles that are allowed to retrieve the token (effectively use the tool). For this to work the Canvas roles that the current user has must also be passed over in the field `canvas_membership_roles`. A user only has to have an overlapping value in these sets to be allowed access. Here's an example of the configuration needed to only allow a Teacher or Tutor  access to a tool:\n```\n    canvas_membership_roles=$Canvas.membership.roles\n    allowed_roles=Teacher,Tutor\n```\n\n## Releasing\n\nTo make a new release of this create a new tag using the maven release plugin. Normally this can be done using:\n\n    mvn release:prepare\n\nThis will ask what then release version should be and then what the next development version should be. We try to follow semantic versioning for this. After the release plugin has finished it should push the changes to GitHub, then GitHub Actions will build the new tag a put a tagged image in the ECR repository. The release to production can then be made by running the production deploy GitHub Action and specifying the newly created tag. Afterwards all this can be cleaned up with:\n\n    mvn release:clean\n\n## Releasing using GitHub actions\n\nThere is a GitHub action to [perform releases](https://github.com/oxctl/tool-support/actions/workflows/release.yml), you can create a Major, Minor or a Patch release, then it gets pushed to production automatically.\n\n## API\n\nThere is now support for CRUD operations to edit tools from curl. Things to note:\n * updates aren't partial - you need to supply all data or it will overwrite and missing fields as null\n * ids for Tool and ToolRegistration models will be ignored when passed as JSON\n\n**CREATE:**\n\n```\ncurl -u \"user:pass1234\" -d '{\n  \"id\": \"93f3fab4-0559-420b-b1d4-9d4a62a896ec\",\n  \"lti\": {\n    \"registrationId\": \"oxeval-cm5\",\n    \"clientName\": \"oxeval-cm2\",\n    \"clientId\": \"1153700000000000432\",\n    \"clientSecret\": \"some_client_secret\",\n    \"clientAuthenticationMethod\": \"client_secret_basic\",\n    \"authorizationGrantType\": \"implicit\",\n    \"redirectUri\": \"{baseUrl}/lti/login\",\n    \"scopes\": [\n      \"openid\"\n    ],\n    \"providerDetails\": {\n      \"authorizationUri\": \"https://sso.canvaslms.com/api/lti/authorize_redirect\",\n      \"tokenUri\": \"https://sso.canvaslms.com/login/oauth2/token\",\n      \"userInfoEndpoint\": {\n        \"uri\": null,\n        \"authenticationMethod\": \"client_secret_basic\",\n        \"userNameAttributeName\": \"sub\"\n      },\n      \"jwkSetUri\": \"https://sso.canvaslms.com/api/lti/security/jwks\",\n      \"issuerUri\": null,\n      \"configurationMetadata\": {}\n    }\n  },\n  \"proxy\": {\n    \"registrationId\": \"oxeval-cm5\",\n    \"clientName\": \"Canvas (oxeval.instructure.com)2\",\n    \"clientId\": \"1153700000000000422\",\n    \"clientSecret\": \"some_client_secret\",\n    \"clientAuthenticationMethod\": \"client_secret_post\",\n    \"authorizationGrantType\": \"authorization_code\",\n    \"redirectUri\": \"{baseUrl}/login/oauth2/code/{registrationId}\",\n    \"scopes\": [],\n    \"providerDetails\": {\n      \"authorizationUri\": \"https://oxeval.instructure.com/login/oauth2/auth\",\n      \"tokenUri\": \"https://oxeval.instructure.com/login/oauth2/token\",\n      \"userInfoEndpoint\": {\n        \"uri\": null,\n        \"authenticationMethod\": \"client_secret_basic\",\n        \"userNameAttributeName\": null\n      },\n      \"jwkSetUri\": null,\n      \"issuerUri\": null,\n      \"configurationMetadata\": {}\n    }\n  },\n  \"origins\": [],\n  \"sign\": false,\n  \"secret\": null,\n  \"issuer\": null,\n  \"nrpsAllowedRoles\": []\n}' -H \"Content-Type: application/json\" -X POST http://localhost:8080/admin/tools/\n```\n\n**READ:**\n\nTo list all the tools:\n```\ncurl -u \"user:pass1234\" http://localhost:8080/admin/tools/\n```\n\nTo get a specific tool by ID:\n```\ncurl -u \"user:pass1234\" http://localhost:8080/admin/tools/04a0aa49-a74f-4dd1-96c5-895c8d68d776\n```\n\nThe API also supports look ups by lti/proxy client ID and registration ID, the supported ID prefixes are:\n - `ltiClientId:`\n - `ltiRegistrationId:`\n - `proxyClientId:`\n - `proxyRegistrationId:`\n\nFor example to lookup by a client ID of `123450000000000678`:\n```\ncurl -u \"user:pass1234\" http://localhost:8080/admin/tools/ltiClientId:123450000000000678\n```\nor for an LTI Registration ID\n\n```\ncurl -u \"user:pass1234\" http://localhost:8080/admin/tools/ltiRegistrationId:my-lovely-lti-tool\n```\n\nAll other operations will need to reference the tool's ID so the value should be copied.\n\n**UPDATE:**\n\nAn update can be made by citing the tool's ID\n\n```\ncurl -u \"user:pass1234\" -d '{\n  \"id\": \"93f3fab4-0559-420b-b1d4-9d4a62a896ec\",\n  \"lti\": {\n    \"registrationId\": \"oxeval-cm27\",\n    \"clientName\": \"oxeval-cm27\",\n    \"clientId\": \"11537000000000004327\",\n    \"clientSecret\": \"client_secret\",\n    \"clientAuthenticationMethod\": \"client_secret\",\n    \"authorizationGrantType\": \"implicit\",\n    \"redirectUri\": \"{baseUrl}/lti/login\",\n    \"scopes\": [\n      \"openid\"\n    ],\n    \"providerDetails\": {\n      \"authorizationUri\": \"https://sso.canvaslms.com/api/lti/authorize_redirect\",\n      \"tokenUri\": \"https://sso.canvaslms.com/login/oauth2/token\",\n      \"userInfoEndpoint\": {\n        \"uri\": null,\n        \"authenticationMethod\": \"client_secret_basic\",\n        \"userNameAttributeName\": \"sub\"\n      },\n      \"jwkSetUri\": \"https://sso.canvaslms.com/api/lti/security/jwks\",\n      \"issuerUri\": null,\n      \"configurationMetadata\": {}\n    }\n  },\n  \"proxy\": {\n    \"registrationId\": \"oxeval-cm27\",\n    \"clientName\": \"Canvas (oxeval.instructure.com)27\",\n    \"clientId\": \"11537000000000004227\",\n    \"clientSecret\": \"some_client_secret\",\n    \"authorizationGrantType\": \"authorization_code\",\n    \"redirectUri\": \"{baseUrl}/login/oauth2/code/{registrationId}\",\n    \"scopes\": [\"scope27\"],\n    \"providerDetails\": {\n      \"authorizationUri\": \"https://oxeval.instructure.com/login/oauth2/auth\",\n      \"tokenUri\": \"https://oxeval.instructure.com/login/oauth2/token\",\n      \"userInfoEndpoint\": {\n        \"uri\": null,\n        \"authenticationMethod\": \"client_secret_basic\",\n        \"userNameAttributeName\": null\n      },\n      \"jwkSetUri\": null,\n      \"issuerUri\": null,\n      \"configurationMetadata\": {}\n    }\n  },\n  \"origins\": [],\n  \"sign\": false,\n  \"secret\": null,\n  \"issuer\": null,\n  \"nrpsAllowedRoles\": []\n}' -H \"Content-Type: application/json\" -X PUT http://localhost:8080/admin/tools/04a0aa49-a74f-4dd1-96c5-895c8d68d776\n```\n\n**DELETE:**\n\nA tool can be deleted by citing the tool's ID\n\n```\ncurl -u \"user:pass1234\" -X DELETE http://localhost:8080/admin/tools/04a0aa49-a74f-4dd1-96c5-895c8d68d776\n```\n\n### Example Sections\n\nWhen creating JSON configuration for a tool you probably just want to use some standard values for the providers. These can be repeated across lots of tools and will often be similar.\n\n#### LTI Registrations\n\nThe minimum (and all you need) for an LTI registration is:\n\n```json\n    {\n      \"registrationId\": \"registration-id\",\n      \"clientName\": \"Client Name\",\n      \"clientId\": \"12345\",\n      \"authorizationGrantType\": \"implicit\",\n      \"redirectUri\": \"{baseUrl}/lti/login\",\n      \"scopes\": [\n        \"openid\"\n      ],\n      \"providerDetails\": {\n        \"authorizationUri\": \"https://sso.canvaslms.com/api/lti/authorize_redirect\",\n        \"tokenUri\": \"https://sso.canvaslms.com/login/oauth2/token\",\n        \"jwkSetUri\": \"https://sso.canvaslms.com/api/lti/security/jwks\"\n      }\n    }\n```\n\n#### LTI Providers\n\nThese values came from https://canvas.instructure.com/doc/api/file.lti_dev_key_config.html and can be put into the `providerDetails` property of the `lti` key.\n\n##### sso.canvaslms.com\n\n```json\n      {\n        \"authorizationUri\": \"https://sso.canvaslms.com/api/lti/authorize_redirect\",\n        \"tokenUri\": \"https://sso.canvaslms.com/login/oauth2/token\",\n        \"jwkSetUri\": \"https://sso.canvaslms.com/api/lti/security/jwks\"\n      }\n```\n\n##### sso.beta.canvaslms.com\n\n```json\n      {\n        \"authorizationUri\": \"https://sso.beta.canvaslms.com/api/lti/authorize_redirect\",\n        \"tokenUri\": \"https://sso.beta.canvaslms.com/login/oauth2/token\",\n        \"jwkSetUri\": \"https://sso.beta.canvaslms.com/api/lti/security/jwks\"\n      }\n```\n\n##### sso.test.canvaslms.com\n\n```json\n      {\n        \"authorizationUri\": \"https://sso.test.canvaslms.com/api/lti/authorize_redirect\",\n        \"tokenUri\": \"https://sso.test.canvaslms.com/login/oauth2/token\",\n        \"jwkSetUri\": \"https://sso.test.canvaslms.com/api/lti/security/jwks\"\n      }\n```\n\n#### Proxy Registration (API)\n\nThe minimum you need for a proxy registration is:\n\n```json\n    {\n      \"registrationId\": \"registration-id\",\n      \"clientName\": \"Client Name\",\n      \"clientId\": \"12345\",\n      \"clientSecret\": \"secret-password\",\n      \"clientAuthenticationMethod\": \"client_secret_post\",\n      \"authorizationGrantType\": \"authorization_code\",\n      \"redirectUri\": \"{baseUrl}/login/oauth2/code/{registrationId}\",\n      \"scopes\": [],\n      \"providerDetails\": {\n        \"authorizationUri\": \"https://instance.instructure.com/login/oauth2/auth\",\n        \"tokenUri\": \"https://instance.instructure.com/login/oauth2/token\"\n      }\n    }\n```\n\nUnlike the LTI registration the provider details will need to be updated for each host as the endpoints are specific to the instance that is being integrated with.\n\n## Spring Boot Actuator \n\nCanvas Tool Support uses Spring Boot Actuator for monitoring. The `health` endpoint is mapped to `/actuator/health`.\n\n## Sentry\n\nApplication errors are reported using https://sentry.io for this application. There is one DSNs to be used for both development and production. There's no DSN for local development. Sentry is setup as early as possible in the application to capture as many errors as possible.\n\n\n## Change Log\nOn Friday 13 June 2025, this repository was relicensed from Apache 2 to MIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxctl%2Ftool-support","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxctl%2Ftool-support","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxctl%2Ftool-support/lists"}