LGTM, thanks again @sijintv !
This Merge Request adds the User Applications REST API, fulfilling the requirements described in #23054.
It introduces a set of CRUD endpoints located at /api/v4/user/applications that allow users to manage their instance-level OAuth applications. This is designed to enable programmatic management of user-owned OAuth applications, which proves extremely useful for dynamically deploying and un-deploying Review Apps without having to employ insecure wildcard redirect URIs.
Specifically, it creates the following endpoints:
POST /api/v4/user/applications: Creates an application, exclusively returning the secret during this action.GET /api/v4/user/applications: Lists all applications owned by the authenticated user.GET /api/v4/user/applications/:id: Retrieves a specific application.PUT /api/v4/user/applications/:id: Updates an existing application (e.g. modifying the redirect_uri).DELETE /api/v4/user/applications/:id: Removes the application.The implementation follows strict security best practices regarding credential exposure and bounded context:
secret is explicitly and exclusively returned only on the POST create event. Subsequent GET or PUT requests will never expose the secret.redirect_uri targets (such as ephemeral Review Apps). This eliminates the insecure practice of users resorting to wildcard redirect URIs to handle dynamic domains.current_user. The Authn::UserApplicationsFinder inherently scopes all GET, PUT, and DELETE requests to only OAuth Applications owned by the authenticated user, completely preventing Insecure Direct Object Reference (IDOR) or cross-user data leakage.The User Applications API executes standard CRUD operations on the oauth_applications table, scoped to the current user. Here are the query execution plans:
EXPLAIN SELECT "oauth_applications".* FROM "oauth_applications" WHERE "oauth_applications"."owner_id" = 1 AND "oauth_applications"."owner_type" = 'User' AND "oauth_applications"."id" = 1 LIMIT 1;
Limit (cost=0.15..2.17 rows=1 width=237)
-> Index Scan using index_oauth_applications_on_owner_id_and_owner_type on oauth_applications (cost=0.15..2.17 rows=1 width=237)
Index Cond: ((owner_id = 1) AND ((owner_type)::text = 'User'::text))
Filter: (id = 1)
EXPLAIN INSERT INTO "oauth_applications" ("name", "uid", "secret", "redirect_uri", "scopes", "created_at", "updated_at", "owner_id", "owner_type", "confidential") VALUES ('test', 'uid', 'secret', 'url', 'api', NOW(), NOW(), 1, 'User', true) RETURNING "id";
Insert on oauth_applications (cost=0.00..0.02 rows=1 width=237)
-> Result (cost=0.00..0.02 rows=1 width=237)
EXPLAIN UPDATE "oauth_applications" SET "name" = 'test2', "updated_at" = NOW() WHERE "oauth_applications"."id" = 1;
Update on oauth_applications (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using oauth_applications_pkey on oauth_applications (cost=0.15..2.17 rows=1 width=46)
Index Cond: (id = 1)
EXPLAIN DELETE FROM "oauth_applications" WHERE "oauth_applications"."id" = 1;
Delete on oauth_applications (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using oauth_applications_pkey on oauth_applications (cost=0.15..2.17 rows=1 width=6)
Index Cond: (id = 1)
The API returns standard GitLab API JSON shapes, and correctly provisions the underlying Authn::OauthApplication record associated with the user.
| Browser Dev Tools Verification |
|---|
![]() |
Ensure your GDK is up and running.
Open your browser developer console while authenticated as any user (e.g. root) on the GitLab dashboard.
Execute the following fetch command to provision an application:
const csrf = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/v4/user/applications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrf
},
body: JSON.stringify({
name: 'Browser UI Test App',
redirect_uri: 'http://localhost/callback',
scopes: 'api',
confidential: false
})
}).then(r => r.json()).then(console.log);
Verify that the response returns a success with the secret and an application_id.
You can assert the creation of the Application by checking your /-/profile/applications interface.
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Isaac Durham (6caec9a9) at 17 Mar 19:50
Clean up templates
@z_painter , good to go!
@adil.farrukh , sorry for all the back and forth. I took another approach, and just linked to the deprecations page which will keep the original content. Does that work for you?
@eread, can you give this a look?
@axil, can you give this a look?
@phillipwells, can you give this a look?
Isaac Durham (2d9a7a5a) at 17 Mar 18:41
Apply suggestions from robot
Isaac Durham (7ef064df) at 17 Mar 18:40
Apply suggestions from robot
Isaac Durham (697aaa95) at 17 Mar 18:40
Apply suggestions from robot
This one seems better with the all as we want to be explicit with something like this.
This seems to have gotten a little stale. Moving work over to Standardize API operation names (!220443 - closed)
Standardizes API operation names in search_admin and secure_files documentation to follow the RESTful API Style Guide.
Get an advanced search migration → Retrieve an advanced search migration
Show secure file details → Retrieve secure file details
Create secure file → Create a secure file
Remove secure file → Delete a secure file
doc/ci/secure_files/_index.md from #show-secure-file-details to #retrieve-secure-file-details
Standardizes the titles and introductions of several API documentation pages. This closes off work from the previous hackathon.
If you are a GitLab team member and only adding documentation, do not add any of the following labels:
~"frontend"~"backend"~"type::bug"~"database"These labels cause the MR to be added to code verification QA issues.
Documentation-related MRs should be reviewed by a Technical Writer for a non-blocking review, based on Documentation Guidelines and the Style Guide.
If you aren't sure which tech writer to ask, use roulette or ask in the #docs Slack channel.
Default behavior, say something like Default behavior when you close an issue.Configuring GDK, say something like Configure GDK.Standardizes the titles and introductions of several API documentation pages. This closes off work from the previous hackathon.
If you are a GitLab team member and only adding documentation, do not add any of the following labels:
~"frontend"~"backend"~"type::bug"~"database"These labels cause the MR to be added to code verification QA issues.
Documentation-related MRs should be reviewed by a Technical Writer for a non-blocking review, based on Documentation Guidelines and the Style Guide.
If you aren't sure which tech writer to ask, use roulette or ask in the #docs Slack channel.
Default behavior, say something like Default behavior when you close an issue.Configuring GDK, say something like Configure GDK.Standardizes the titles and introductions of several API documentation pages. This closes off work from the previous hackathon.
If you are a GitLab team member and only adding documentation, do not add any of the following labels:
~"frontend"~"backend"~"type::bug"~"database"These labels cause the MR to be added to code verification QA issues.
Documentation-related MRs should be reviewed by a Technical Writer for a non-blocking review, based on Documentation Guidelines and the Style Guide.
If you aren't sure which tech writer to ask, use roulette or ask in the #docs Slack channel.
Default behavior, say something like Default behavior when you close an issue.Configuring GDK, say something like Configure GDK.