Allow ai_workflows tokens to read award emoji

What does this MR do and why?

Composite identity tokens issued for Duo Agent Platform custom flows carry only the ai_workflows scope, which neither API surface accepted for award emoji. REST returned 403 insufficient_scope. GraphQL failed silently: Note.awardEmoji resolved to null with no entry in the errors array, so a client could not tell "reactions are unreadable" apart from "there are no reactions", and automation could treat a thumbs-downed comment as having received no feedback.

Declare the scope on the REST endpoints (read-only) and on the GraphQL field, its type, and that type's own fields. Policy checks are unchanged: AwardEmojiPolicy still delegates read_emoji to the awardable's read ability, so these tokens gain no visibility beyond what they are already permitted to see on the parent awardable or note.

The REST declaration is class-level and therefore also covers the group epic routes that EE appends. That stays within the existing boundary, because epics already allow ai_workflows access and so do epic notes.

References

#609265 (closed)

How to set up and validate locally

  1. In the Rails console, mint an OAuth token carrying only the ai_workflows scope. Use a non-admin user with Reporter or higher access to a private project:

    user = User.find_by_username!('<your-username>')
    organization = user.organization
    
    app = Authn::OauthApplication.create!(
      name: 'ai-workflows-award-emoji-check',
      redirect_uri: Gitlab::Routing.url_helpers.root_url,
      owner: user,
      organization: organization,
      scopes: 'ai_workflows',
      trusted: true,
      confidential: true
    )
    
    token = OauthAccessToken.create!(
      application: app,
      resource_owner_id: user.id,
      organization: organization,
      scopes: [:ai_workflows],
      expires_in: 2.hours
    )
    
    puts token.plaintext_token
  2. As that user, add a reaction to a note on a merge request in that project, and leave a second note without any reaction. Print both identifiers — REST takes the numeric ID, GraphQL takes the global ID:

    [Note.find(<reacted_note_id>), Note.find(<plain_note_id>)].each do |note|
      puts [note.id, note.to_global_id].join(' ')
    end
  3. Read the reactions over REST:

    curl --include \
      --header "Authorization: Bearer <token>" \
      --url "http://gdk.test:3000/api/v4/projects/<project_id>/merge_requests/<mr_iid>/notes/<note_id>/award_emoji"

    Expect 200 OK and the reaction. HEAD on the same URL, and GET on .../award_emoji/<award_id>, also return 200 OK. Before this MR, all return 403 insufficient_scope.

  4. Read the reactions over GraphQL. Send the query to /api/graphql with the same Authorization: Bearer <token> header — a signed-in GraphiQL session uses your session instead of the token, and validates nothing:

    query {
      note(id: "<note_global_id>") {
        id
        awardEmoji(first: 100) {
          nodes {
            name
            user {
              username
            }
          }
        }
      }
    }

    Expect the reaction nodes, and { "awardEmoji": { "nodes": [] } } for the note without reactions. Before this MR, awardEmoji is null with no entry in errors — which is what makes the failure indistinguishable from "this note has no reactions".

  5. Verify the scope stays read-only. POST and DELETE against the same endpoints still return 403 insufficient_scope, and the reaction list is unchanged afterwards. The error body now advertises "scope":"ai_workflows api read_api", so the write verbs are excluded by the request.get? || request.head? condition rather than by the scope being absent.

All five steps were run against a local GDK on this branch, as a non-admin Reporter on a private project. Group epic routes are covered by the same class-level declaration, but need a Premium license, so they are left to the request specs in ee/spec/requests/api/award_emoji_spec.rb.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Xingli Wu

Merge request reports

Loading
Loading