Skip to content

feat(profile): endpoint mobile de leitura do perfil (Feature 3) - #566

Draft
tecrodrigocastro wants to merge 8 commits into
he4rt:4.xfrom
tecrodrigocastro:story/531-api-mobile-profile
Draft

tecrodrigocastro wants to merge 8 commits into
he4rt:4.xfrom
tecrodrigocastro:story/531-api-mobile-profile

Conversation

@tecrodrigocastro

@tecrodrigocastro tecrodrigocastro commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Draft — não revisar ainda. O diff mostra 41 arquivos / ~1700 linhas porque esta branch está empilhada em cima da story/531-api-mobile-auth-jwt (#565), que ainda não está na 4.x. O escopo real desta PR é só 4 arquivos novos do módulo profile (controller, resource, rota, teste) — veja a lista abaixo. Assim que a #565 mergear, o GitHub recalcula o diff sozinho e esta PR passa a mostrar só isso. Vou tirar do draft nesse momento.

Depende da #565 (merge primeiro)

Não mergear antes da #565.

Escopo real (o que muda de fato)

  • app-modules/profile/routes/api-mobile-routes.php (novo)
  • app-modules/profile/src/Http/Controllers/Mobile/MobileProfileController.php (novo)
  • app-modules/profile/src/Http/Resources/ProfileResource.php (novo)
  • app-modules/profile/tests/Feature/Http/MobileProfileControllerTest.php (novo)
  • docs/plans/2026-09-22-api-mobile-jwt.md (2 linhas, marca a Feature 3 como implementada)

Summary

Feature 3 do plano (docs/plans/2026-09-22-api-mobile-jwt.md): endpoint de leitura do perfil do usuário autenticado para o app mobile.

  • GET /api/mobile/profile (atrás de auth:api) retorna o perfil via ProfileResource: dados profissionais (nickname, headline, seniority, anos de experiência, about, social_links), disponibilidade/preferências, skills e experiências profissionais.
  • v1 é só leitura, conforme o PRD ("visualização do próprio perfil").
  • MobileProfileController reaproveita Profile::ensureExists(auth()->id()), já usado no resto do domínio.
  • Corrigido um detalhe do ResourceResponse padrão do Laravel: quando o profile é criado na hora (primeiro acesso), ele devolveria 201 numa rota GET — forçado 200 explicitamente.

Test plan

  • vendor/bin/pint --dirty
  • vendor/bin/phpstan analyse app-modules/profile/src --memory-limit=1G (0 erros)
  • vendor/bin/pest app-modules/profile/tests (73 testes passando, incluindo os 3 novos do endpoint)
  • Testado manualmente via tinker (payload do ProfileResource conferido)

Guard "api" (php-open-source-saver/jwt-auth): endpoints de redirect/callback
OAuth (Discord/GitHub/Twitch) que devolvem um código de troca de uso único
via deep link, GET /api/mobile/me, refresh e logout. Implementa a Feature 0
de docs/plans/2026-09-22-api-mobile-jwt.md.

Refs he4rt#531
… contra a app real

Achados testando os endpoints de verdade (não só Pest):

- Discord/GitHub/Twitch só conhecem UMA redirect_uri fixa por app, o
  callback web (/auth/oauth/{provider}). MobileOAuthController::callback
  nunca seria chamado de verdade. O callback web agora distingue o login
  mobile via OAuthIntent::MobileLogin (lido do state) e finaliza com
  código de troca + deep link em vez de duplicar rota de callback.
- Qualquer request pra rota auth:api sem "Accept: application/json" dava
  500 em vez de 401: o middleware padrão tenta redirect(route('login')),
  que este app não tem (login é só OAuth). Fix em bootstrap/app.php.

Suíte inteira (1749 testes) verde depois da mudança global.
Marca rotas auth:* como bearer na doc (scramble.security_strategy —
não vinha configurado) e adiciona resumo/descrição nos endpoints
mobile pro Stoplight Elements em /docs/3.x/api. Tipa o retorno de
MobileTokenDTO::toArray() como array-shape pra schema de resposta
mais preciso.

Verificado contra o /docs/3.x/swagger.json gerado: os 5 endpoints
aparecem, com bearer marcado corretamente em /me, /logout e no
default do documento (redirect e exchange ficam públicos, como
deveria).
O módulo he4rt/docs nasceu (he4rt#123) quando a branch principal do repo
ainda era 3.x — a string ficou hardcoded em 5 lugares e nunca
acompanhou o bump pra 4.x. Move resources/docs/3.x pro caminho novo,
atualiza Documentation.php, DocsServiceProvider.php, config/docs.php
e config/scramble.php, e o teste que trava a URL do Scramble.

/docs/4.x/api agora serve a doc da API mobile gerada nesta branch.
…bit)

Cache::pull() é get()+forget() como duas chamadas separadas — duas
requisições concorrentes com o mesmo código podiam ambas ler o valor
antes de qualquer uma apagar, mintando dois tokens da mesma
autorização (CWE-367). ExchangeMobileCodeAction agora serializa
leitura+remoção com Cache::lock().

Atualiza o plano com o contrato real (refresh usa o mesmo JWT, sem
refresh_token separado; não existe rota de callback mobile própria)
e documenta como débito técnico conhecido o outro achado da revisão:
o deep link por custom scheme pode ser sequestrado por outro app no
aparelho — a correção certa é PKCE, mas depende do he4rt-app também
mudar, então não é fechada nesta PR.
- Remove comentário narrativo e o returnUrl 'mobile' morto — o fluxo
  mobile nunca chega a ler returnUrl, então null (já aceito pelo tipo)
  é mais honesto que uma string mágica sem uso.
- getAuthenticate: centraliza a checagem de OAuthIntent::MobileLogin
  numa variável ($isMobile) calculada uma vez em vez de repetida em 3
  pontos do método.
GET /api/mobile/profile retorna o perfil do usuário autenticado
(profissional, skills e experiências), via ProfileResource. v1
só leitura, conforme o PRD.

Depende da story/531-api-mobile-auth-jwt (guard JWT `api`).
@tecrodrigocastro
tecrodrigocastro requested a review from a team September 25, 2026 14:28
@tecrodrigocastro
tecrodrigocastro marked this pull request as draft September 25, 2026 14:29
@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: b2fdc3f0-5909-484f-8ebb-7f7e55e2f1c4

📥 Commits

Reviewing files that changed from the base of the PR and between dbf33db and 39d9de6.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (40)
  • .env.example
  • .env.testing.example
  • app-modules/docs/config/docs.php
  • app-modules/docs/routes/docs-routes.php
  • app-modules/docs/src/DocsServiceProvider.php
  • app-modules/docs/src/Documentation.php
  • app-modules/docs/tests/Feature/Discovery/DocsRoutingTest.php
  • app-modules/identity/routes/api-mobile-routes.php
  • app-modules/identity/src/Auth/Actions/ExchangeMobileCodeAction.php
  • app-modules/identity/src/Auth/Actions/HandleOAuthCallbackAction.php
  • app-modules/identity/src/Auth/Actions/IssueMobileExchangeCodeAction.php
  • app-modules/identity/src/Auth/Actions/IssueMobileTokenAction.php
  • app-modules/identity/src/Auth/DTOs/MobileTokenDTO.php
  • app-modules/identity/src/Auth/Enums/OAuthIntent.php
  • app-modules/identity/src/Auth/Exceptions/MobileAuthException.php
  • app-modules/identity/src/Auth/Http/Controllers/Mobile/MobileAuthController.php
  • app-modules/identity/src/Auth/Http/Controllers/Mobile/MobileMeController.php
  • app-modules/identity/src/Auth/Http/Controllers/Mobile/MobileOAuthController.php
  • app-modules/identity/src/Auth/Http/Controllers/OAuthController.php
  • app-modules/identity/src/Auth/Support/MobileOAuthDeepLink.php
  • app-modules/identity/src/User/Models/User.php
  • app-modules/identity/tests/Feature/Auth/MobileAuthControllerTest.php
  • app-modules/identity/tests/Feature/Auth/MobileOAuthControllerTest.php
  • app-modules/profile/routes/api-mobile-routes.php
  • app-modules/profile/src/Http/Controllers/Mobile/MobileProfileController.php
  • app-modules/profile/src/Http/Resources/ProfileResource.php
  • app-modules/profile/tests/Feature/Http/MobileProfileControllerTest.php
  • bootstrap/app.php
  • composer.json
  • config/auth.php
  • config/jwt.php
  • config/scramble.php
  • config/services.php
  • docs/plans/2026-09-22-api-mobile-jwt.md
  • resources/docs/4.x/convencoes-de-codigo.md
  • resources/docs/4.x/documentation.md
  • resources/docs/4.x/installation.md
  • resources/docs/4.x/primeiro-pull-request.md
  • resources/docs/4.x/releases.md
  • resources/docs/4.x/rodando-o-projeto.md
Files not reviewed due to moderation or processing errors (29)
  • .env.example
  • .env.testing.example
  • composer.json
  • config/auth.php
  • config/jwt.php
  • app-modules/identity/src/User/Models/User.php
  • config/services.php
  • bootstrap/app.php
  • config/scramble.php
  • docs/plans/2026-09-22-api-mobile-jwt.md
  • app-modules/identity/src/Auth/Enums/OAuthIntent.php
  • app-modules/identity/src/Auth/Actions/HandleOAuthCallbackAction.php
  • app-modules/identity/src/Auth/Exceptions/MobileAuthException.php
  • app-modules/identity/src/Auth/Actions/IssueMobileExchangeCodeAction.php
  • app-modules/identity/src/Auth/Actions/ExchangeMobileCodeAction.php
  • app-modules/identity/src/Auth/Support/MobileOAuthDeepLink.php
  • app-modules/identity/src/Auth/Http/Controllers/Mobile/MobileOAuthController.php
  • app-modules/identity/src/Auth/Http/Controllers/OAuthController.php
  • app-modules/identity/tests/Feature/Auth/MobileOAuthControllerTest.php
  • app-modules/identity/routes/api-mobile-routes.php
  • app-modules/identity/src/Auth/DTOs/MobileTokenDTO.php
  • app-modules/identity/src/Auth/Actions/IssueMobileTokenAction.php
  • app-modules/identity/src/Auth/Http/Controllers/Mobile/MobileAuthController.php
  • app-modules/identity/src/Auth/Http/Controllers/Mobile/MobileMeController.php
  • app-modules/identity/tests/Feature/Auth/MobileAuthControllerTest.php
  • app-modules/profile/routes/api-mobile-routes.php
  • app-modules/profile/src/Http/Controllers/Mobile/MobileProfileController.php
  • app-modules/profile/src/Http/Resources/ProfileResource.php
  • app-modules/profile/tests/Feature/Http/MobileProfileControllerTest.php

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

Adds JWT-backed mobile authentication through OAuth, including short-lived exchange codes, token exchange, refresh, logout, and current-user endpoints. Adds an authenticated mobile profile endpoint with serialized profile, skill, and work-experience data. Adds JWT and deep-link configuration, exception handling, and feature tests. Updates API documentation configuration, routes, tests, and examples from 3.x to 4.x.

Priority: ⬇️ Low

Estimated code review effort:

Suggested reviewers: danielhe4rt

Change: Feature

Merge Risk: 🔵 Low · up to 39d9d

The profile endpoint has no established blocking issue in the supplied evidence. The mobile OAuth handoff still warrants coordination with the app before rollout because custom-scheme links may be intercepted.

Security Architecture Review

Security architecture risk: 🟠 High · up to 39d9d

The new mobile sign-in flow sends a short-lived login code through a configurable app link. If another app receives that link, it could exchange the code for the user’s token. The example cache setting also does not provide the shared storage this authentication flow needs. The actual mobile-link handling and production cache settings remain unverified.

Retained concerns

  • High · security · inferred: The new callback places a redeemable login code in a configurable custom-scheme link. If another installed app receives that link before the intended app, it can use the public exchange endpoint to obtain the user’s JWT; the server does not bind redemption to the initiating client or device. Actual interception depends on mobile-platform and app-link handling not present in the evidence.
  • Medium · security · inferred: The newly cache-dependent authentication flow has no established shared production cache in the supplied evidence. If deployed with the example array cache, exchange codes and locks are not shared across workers or requests, and cache-backed JWT revocation cannot provide a reliable cross-worker logout guarantee.
Security review details

Security Blast Radius

  • inferred — A successfully intercepted exchange code yields a JWT for its associated user, exposing that user’s authenticated mobile endpoints, including their profile. The evidence does not establish tenant-wide or administrative authority.

Security Findings and Attack Paths

  • inferred — The new bearer-code link creates a conditional interception path from mobile link handling to JWT issuance. The supplied evidence neither demonstrates interception on the intended mobile platform nor establishes a client-side control that rules it out.

Trust Boundaries and Controls

  • observed — Mobile OAuth limits redirect initiation to supported providers and uses encrypted state. The handoff code is random, expires after 60 seconds, and is normally consumed once under a per-code lock; exchange and refresh are public, whereas logout, current-user, and profile routes require API authentication.
  • observed — Provider identity resolution first matches an existing external identity, then falls back to matching an account by email. This matching action is unchanged in the target diff; provider-specific email assurance is not established uniformly by the inspected clients.

Resilience and Maintainability Implications

  • inferred — The at-most-once handoff depends on a cache lock shared by all exchange workers and on its 10-second lease covering the read-and-delete operation. The supplied configuration does not establish those production conditions.

Hardening Proposals

  • proposed — Use a mobile callback with verified app ownership and bind code redemption to the initiating client; validate the deployed link behavior against another app claiming the configured scheme.
  • proposed — Specify and verify a shared production cache for exchange locks, codes, and JWT revocation before enabling mobile authentication across workers.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.26% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 31 functions across 30 files. (6 skipped:… 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.
Title check ✅ Passed The title clearly identifies the main change: a mobile profile read endpoint. It matches the pull request scope.
Description check ✅ Passed The description provides the context, scope, endpoint behavior, dependency on #565, implementation details, and completed test steps. The omitted evidence section is acceptable because the change has …
Full details: Docstring Coverage

Explanation

Docstring coverage is 32.26% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 31 functions across 30 files. (6 skipped: 6 unsupported.)

  • Fix all pre-merge checks with AI

Warning

Review coverage is incomplete: 29 files could not be fully reviewed. Findings from completed review steps are included; see review info for details.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant