Skip to content

Feature EP-765 User Identity based on a Bearer access token - #1181

Merged
wazelin merged 7 commits into
developfrom
feature/EP-693/personalized-access-tokens
Aug 6, 2026
Merged

Feature EP-765 User Identity based on a Bearer access token#1181
wazelin merged 7 commits into
developfrom
feature/EP-693/personalized-access-tokens

Conversation

@wazelin

@wazelin wazelin commented Aug 5, 2026

Copy link
Copy Markdown
Member

Ticket: EP-765

What's Changed

This adds support for User Identity based on a bearer access token

Please tick the appropriate points

  • Ticket attached or not required
  • Breaking change
  • Configuration change
  • Release version change
  • Tests are running successfully (old and new ones) on my local machine (if applicable)
  • New code is respecting code style rules
  • New code is respecting best practices
  • New code is not subject to concurrency issues (if applicable)
  • Feature is working correctly on my local machine (if applicable)
  • Acceptance criteria are respected
  • Pull request title and description are meaningful

Summary by CodeRabbit

  • New Features

    • Sessions can now be established using access tokens, including user identity, login, and role information.
    • Added support for authenticated users represented by identifiers, roles, and login details.
    • Access-token sessions fall back to existing session handling when no valid token is provided.
  • Compatibility

    • Added PHP 8.5 to supported continuous-integration test coverage.
  • Tests

    • Added coverage for access-token parsing, request extraction, and authenticated session creation.
    • Added validation for authenticated sessions with and without assigned roles.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The session manager now creates authenticated sessions from JWT access tokens. BasicUser stores token-derived identity data. CI and configuration tests now target PHP 8.5.

Changes

Session authentication

Layer / File(s) Summary
User model and token session flow
common/oatbox/user/BasicUser.php, common/session/class.SessionManager.php, composer.json
Adds BasicUser and token-based session creation. JWT parsing, access-token extraction, identity construction, session caching, and the JWT development dependency are included.
Token session validation
test/unit/common/session/SessionManagerTest.php
Adds signed JWT tests for token parsing, request extraction, and users with or without roles.
PHP 8.5 compatibility
.github/workflows/continuous-integration.yaml, test/unit/ConfigurationTest.php
Adds PHP 8.5 CI coverage and updates configuration tests to use imported classes and PHP 8.5 fixtures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Request
  participant SessionManager
  participant BasicUser
  participant BasicSession
  Request->>SessionManager: Provide Authorization access token
  SessionManager->>SessionManager: Parse token claims
  SessionManager->>BasicUser: Build user identity
  SessionManager->>BasicSession: Create authenticated session
Loading

Suggested labels: ⏱️ 10-30 Min Review

Suggested reviewers: bartlomiejmarszal, aloha202, kilatib

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: user identity based on bearer access tokens.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/EP-693/personalized-access-tokens

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@common/oatbox/user/BasicUser.php`:
- Line 25: Add a directly preceding DocBlock to the BasicUser class declaration,
including the required license information specified by the repository’s path
instructions. Do not alter the class implementation or its User interface
contract.
- Line 1: Add strict typing declarations in both affected files: place
declare(strict_types=1); immediately after the PHP opening tag in
common/oatbox/user/BasicUser.php, and after the file header before imports in
common/session/class.SessionManager.php.

In `@common/session/class.SessionManager.php`:
- Around line 128-150: Update parseAccessToken() to strictly validate Base64URL
decoding and JSON decoding, returning null for invalid input without suppressing
errors, and ensure the decoded payload is an array. In
createAccessTokenSession(), require token['user']['login'] to be a string before
passing it to preg_match(), so malformed tokens fall back to anonymous session
handling.

In `@test/unit/common/session/SessionManagerTest.php`:
- Around line 53-84: Extend SessionManagerTest with isolated negative cases
covering tampered signatures, expired tokens, malformed payloads, and non-string
user.login values. Build each invalid token using the existing token helpers,
invoke SessionManager::getSession(), and assert it does not create a BasicUser;
retain the existing valid-token tests unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: https://raw.githubusercontent.com/oat-sa/tao-code-quality/main/coderabbit/php/authoring/v1/.coderabbit.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: a063695f-a5e2-4529-9edd-5db7393eddea

📥 Commits

Reviewing files that changed from the base of the PR and between d4c8002 and 8e946bb.

📒 Files selected for processing (6)
  • .github/workflows/continuous-integration.yaml
  • common/oatbox/user/BasicUser.php
  • common/session/class.SessionManager.php
  • composer.json
  • test/unit/ConfigurationTest.php
  • test/unit/common/session/SessionManagerTest.php

Comment thread common/oatbox/user/BasicUser.php
Comment thread common/oatbox/user/BasicUser.php
Comment thread common/session/class.SessionManager.php
Comment thread test/unit/common/session/SessionManagerTest.php
@wazelin
wazelin force-pushed the feature/EP-693/personalized-access-tokens branch from 8e946bb to b74546e Compare August 5, 2026 11:19
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@wazelin
wazelin force-pushed the feature/EP-693/personalized-access-tokens branch from b74546e to 7994ca2 Compare August 5, 2026 11:23
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@wazelin
wazelin force-pushed the feature/EP-693/personalized-access-tokens branch from 7994ca2 to 5616ec5 Compare August 5, 2026 11:40
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@common/session/class.SessionManager.php`:
- Around line 18-22: Add declare(strict_types=1); immediately after the opening
PHP tag in SessionManager.php, before the namespace or imports.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: https://raw.githubusercontent.com/oat-sa/tao-code-quality/main/coderabbit/php/authoring/v1/.coderabbit.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 226a15fa-e2e2-4ee1-9a7c-d590bafd7bcc

📥 Commits

Reviewing files that changed from the base of the PR and between d4c8002 and 5616ec5.

📒 Files selected for processing (6)
  • .github/workflows/continuous-integration.yaml
  • common/oatbox/user/BasicUser.php
  • common/session/class.SessionManager.php
  • composer.json
  • test/unit/ConfigurationTest.php
  • test/unit/common/session/SessionManagerTest.php
🚧 Files skipped from review as they are similar to previous changes (4)
  • common/oatbox/user/BasicUser.php
  • .github/workflows/continuous-integration.yaml
  • composer.json
  • test/unit/ConfigurationTest.php

Comment thread common/session/class.SessionManager.php Outdated
@wazelin
wazelin force-pushed the feature/EP-693/personalized-access-tokens branch from 5616ec5 to ea2673b Compare August 5, 2026 11:50
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
common/session/class.SessionManager.php (1)

130-133: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the suppressed unused destructuring variable.

Replace the destructuring assignment with an indexed payload lookup. This removes the PHPMD warning and avoids suppressing malformed-token notices.

Proposed refactor
-        /** `@noinspection` PhpUnusedLocalVariableInspection */
-        @[$_, $payload] = explode('.', $accessToken);
+        $parts = explode('.', $accessToken, 3);
+        $payload = $parts[1] ?? '';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@common/session/class.SessionManager.php` around lines 130 - 133, Update the
access-token parsing in SessionManager to replace the suppressed destructuring
assignment with an indexed lookup of the payload segment from explode('.').
Preserve the existing fallback for a missing payload when decoding, and remove
the PhpUnusedLocalVariableInspection suppression.

Source: Linters/SAST tools

test/unit/common/session/SessionManagerTest.php (1)

90-93: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Align the test name with the behavior it verifies.

testGetCookieBasedSession() does not set a cookie or a PHP-session attribute. It only checks the anonymous fallback when no access token exists. Rename it to testGetAnonymousSession() or initialize cookie/session state and assert the expected session.

As per path instructions, tests must exercise the behavior they claim to cover.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/common/session/SessionManagerTest.php` around lines 90 - 93, Rename
testGetCookieBasedSession() to testGetAnonymousSession() so the test name
accurately describes its existing behavior of verifying the anonymous fallback
without cookie or PHP-session state.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@common/session/class.SessionManager.php`:
- Around line 130-133: Update the access-token parsing in SessionManager to
replace the suppressed destructuring assignment with an indexed lookup of the
payload segment from explode('.'). Preserve the existing fallback for a missing
payload when decoding, and remove the PhpUnusedLocalVariableInspection
suppression.

In `@test/unit/common/session/SessionManagerTest.php`:
- Around line 90-93: Rename testGetCookieBasedSession() to
testGetAnonymousSession() so the test name accurately describes its existing
behavior of verifying the anonymous fallback without cookie or PHP-session
state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: https://raw.githubusercontent.com/oat-sa/tao-code-quality/main/coderabbit/php/authoring/v1/.coderabbit.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 93211c19-75e8-4270-b244-adf7220c781c

📥 Commits

Reviewing files that changed from the base of the PR and between d4c8002 and ea2673b.

📒 Files selected for processing (6)
  • .github/workflows/continuous-integration.yaml
  • common/oatbox/user/BasicUser.php
  • common/session/class.SessionManager.php
  • composer.json
  • test/unit/ConfigurationTest.php
  • test/unit/common/session/SessionManagerTest.php
🚧 Files skipped from review as they are similar to previous changes (4)
  • .github/workflows/continuous-integration.yaml
  • composer.json
  • test/unit/ConfigurationTest.php
  • common/oatbox/user/BasicUser.php

@wazelin
wazelin force-pushed the feature/EP-693/personalized-access-tokens branch from ea2673b to 797e887 Compare August 5, 2026 13:04
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@common/session/class.SessionManager.php`:
- Around line 122-139: Remove the new static helpers
extractAccessTokenFromRequest(), parseAccessToken(), and
buildUserIdentityString() from SessionManager. Move authorization-header access
and token parsing into injected collaborators, and update SessionManager to
consume those dependencies instead of global $_SERVER state or static utility
calls; preserve the existing identity-string behavior through the injected
design.
- Around line 136-161: Align buildUserIdentityString() and
createAccessTokenSession() on one unambiguous identity grammar: ensure the
serialized role#user form is parsed into the role and userId components,
including roles without an embedded '#'. Update the parsing regex and capture
handling accordingly, then add a test covering buildUserIdentityString('role',
'user') and verifying BasicUser receives the expected role and identifier.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: https://raw.githubusercontent.com/oat-sa/tao-code-quality/main/coderabbit/php/authoring/v1/.coderabbit.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: d48c113c-c893-425c-ac24-83e616823d70

📥 Commits

Reviewing files that changed from the base of the PR and between d4c8002 and 797e887.

📒 Files selected for processing (6)
  • .github/workflows/continuous-integration.yaml
  • common/oatbox/user/BasicUser.php
  • common/session/class.SessionManager.php
  • composer.json
  • test/unit/ConfigurationTest.php
  • test/unit/common/session/SessionManagerTest.php
🚧 Files skipped from review as they are similar to previous changes (5)
  • .github/workflows/continuous-integration.yaml
  • common/oatbox/user/BasicUser.php
  • test/unit/common/session/SessionManagerTest.php
  • composer.json
  • test/unit/ConfigurationTest.php

Comment thread common/session/class.SessionManager.php
Comment thread common/session/class.SessionManager.php
@wazelin wazelin changed the title Feature EP-693 personalized access tokens Feature EP-693 User Identity based on a Bearer access token Aug 5, 2026
@wazelin wazelin changed the title Feature EP-693 User Identity based on a Bearer access token Feature EP-765 User Identity based on a Bearer access token Aug 5, 2026

@shpran shpran left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread common/oatbox/user/BasicUser.php Outdated
Comment thread common/session/class.SessionManager.php Outdated
Comment thread common/session/class.SessionManager.php Outdated
Comment thread composer.json
wazelin and others added 2 commits August 5, 2026 18:11
Co-authored-by: Andrei Shapiro <59471572+shpran@users.noreply.github.com>
Signed-off-by: Sergei Mikhailov <contact@sergeimikhailov.com>
Comment thread common/session/class.SessionManager.php Outdated
Comment thread common/session/class.SessionManager.php
Comment thread common/session/class.SessionManager.php
Comment thread common/session/class.SessionManager.php
Comment thread common/session/class.SessionManager.php

public static function extractAccessTokenFromRequest(): string
{
$authorizationHeader = explode(' ', $_SERVER['HTTP_AUTHORIZATION'] ?? '', 2);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use $request = common_http_Request::currentRequest() (from tao/generis/common/http/class.Request.php) to fetch the header using $header = $request->getHeaderValue('Authorization');
Lets don't use globals if not needed.

@wazelin wazelin Aug 6, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread common/session/class.SessionManager.php
Comment thread common/oatbox/user/BasicUser.php Outdated
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LICENCE

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are we, Brits? :D

Comment thread common/session/class.SessionManager.php Outdated
* SPDX-FileCopyrightText: 2013-2026 Open Assessment Technologies S.A.
* Copyright (C) 2013-2026 (original work) Open Assessment Technologies S.A.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-TAO-Commercial-License

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This extension is not in a list in OCE-202

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's weird. How can we skip generis out of this?

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Version

Target Version 17.3.0
Last version 17.2.0

There are 0 BREAKING CHANGE, 2 features, 0 fix

@Karol-Stelmaczonek
Karol-Stelmaczonek self-requested a review August 6, 2026 13:48
@wazelin
wazelin merged commit 4204232 into develop Aug 6, 2026
6 checks passed
@wazelin
wazelin deleted the feature/EP-693/personalized-access-tokens branch August 6, 2026 15:39
@oat-github-bot oat-github-bot mentioned this pull request Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants