본문으로 건너뛰기
참조

HTTP 미들웨어

App Route stack, 등록 별칭, 인자, 요청 효과, 거부 코드, 호스트 전용 경계를 정확히 찾아봅니다.

HTTP 미들웨어

생성된 외부 route stack을 유지하고 동작에 필요한 권한 가드를 추가하세요. 최소 예시에서 순서를, alias 표에서 인자와 거부 응답을 확인할 수 있습니다. 조직 문맥은 좌표를 제공하고 권한 가드는 행위자의 실행 가능 여부를 판단합니다.

시그니처

App은 App\Http\Middleware를 import하지 않습니다. 패키지 경계를 건너는 안정된 이름은 packages/app-sdk/packages/laravel/src/Http/Middleware.php에서 SDK가 공개합니다.

코드 예시
PHP
final class Middleware
{
    public const AGENT_DELEGATION = 'agent.delegation';
    public const APP_INSTALLED = 'app.installed';
    public const AUTHENTICATED_LOCALE = 'nexia.locale';
    public const LEGAL_ENTITY_CONTEXT = 'nexia.context.legal_entity';
    public const WORKSPACE_CONTEXT = 'nexia.context.workspace';
}

인자가 있는 권한·구조 guard는 호스트가 등록한 문자열 시그니처입니다. 이 문서에 있는 시그니처만 사용하세요. Core에 미들웨어 클래스가 존재한다는 사실은 그 클래스를 App 의존성으로 만들지 않습니다.

최소 예시

다음은 stubs/package-app/empty-routes.stub이 만드는 바깥 stack에 실제 App 키를 넣은 전체 형태입니다. 테넌트를 찾고 세션을 격리한 다음, 사람 또는 위임된 Agent를 인증하고, 위임 권한을 좁히고, App 가용성을 확인한 뒤 화면과 조직 컨텍스트를 설정합니다.

코드 예시
PHP
use Illuminate\Support\Facades\Route;
use Nexia\Http\Middleware;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
use Stancl\Tenancy\Middleware\PreventAccessFromUnwantedDomains;
use Stancl\Tenancy\Middleware\ScopeSessions;

Route::middleware([
    'web',
    InitializeTenancyByDomainOrSubdomain::class,
    PreventAccessFromUnwantedDomains::class,
    ScopeSessions::class,
    'auth:sanctum,agent',
    'agent.delegation',
    'app.installed:workshop',
    Middleware::LEGAL_ENTITY_CONTEXT,
    Middleware::WORKSPACE_CONTEXT,
])->group(function (): void {
    // Workshop 경로를 여기에 등록합니다.
});

그룹 안의 각 연산을 인가하세요. 생성된 tenant 경로는 can.tenant_wide를 사용합니다. 정식 Legal Entity 경로는 Controller에서 요청 대상 또는 저장된 소유자를 해석해 인가하고, URL에 대상이 있는 경로는 can.scope를 사용합니다. 바깥 stack은 신원과 문맥만 확립합니다.

인자

생성되는 바깥 stack

실행 순서도 계약의 일부입니다. 테넌트를 식별하기 전에 인증을 안전하게 평가할 수 없고, 위임 요청은 capability guard나 controller가 실행되기 전에 먼저 좁혀야 합니다.

항목인자필수런타임 효과
web없음예브라우저·세션 stack을 시작합니다. Core는 유효한 support session 재검사를 추가하고 기본 CSRF 미들웨어를 bearer-aware 하위 클래스로 교체합니다.
InitializeTenancyByDomainOrSubdomain::class없음예요청 host에서 테넌트를 찾아 해당 database, cache, filesystem, URL 컨텍스트로 진입합니다.
PreventAccessFromUnwantedDomains::class없음예해석된 테넌트에 유효하지 않은 도메인을 거부합니다.
ScopeSessions::class없음예한 테넌트 세션이 다른 테넌트 신원에서 재사용되지 않게 합니다.
auth:sanctum,agentguard 목록예Sanctum 사람 세션 또는 서명된 Agent 위임 토큰을 받습니다. 인증만으로는 아무 권한도 생기지 않습니다.
Middleware::AGENT_DELEGATION없음Agent 호출 가능 Route에 필수위임 요청에서 토큰 allowlist, 서명된 Legal Entity·Operating Unit 대상, 실시간 도구 정책의 교집합을 적용합니다. 사람 요청에서는 아무 일도 하지 않습니다.
Middleware::APP_INSTALLED.':{app_key}'app_key예App과 모든 전이 prerequisite가 테넌트에서 operational인지 확인합니다.
Middleware::LEGAL_ENTITY_CONTEXT고정 Route 이름 legalEntity생성 stack에 필수명시적인 Route·위임 대상을 쓰고, 없으면 유효한 세션·기본 선호를 복원합니다. 권한을 부여하지 않습니다.
Middleware::WORKSPACE_CONTEXT없음생성 stack에 필수접근 가능한 Workspace 선호를 복원하거나 지웁니다. Workspace는 화면 상태이며 권한 범위가 아닙니다.

등록된 Nexia 별칭

bootstrap/app.php는 커스텀 별칭 열네 개를 등록합니다. 열 개는 App Route에서 사용하고 네 개는 플랫폼 전용 진입점에 속합니다.

별칭 시그니처인자와 기본값사용 주체동작
agent.delegation없음App·Core API 그룹위임된 Agent 권한을 좁힙니다. 사람의 Sanctum 요청은 그대로 통과합니다.
app.installed:{app_key}필수 App 키App RouteApp prerequisite closure 전체가 operational이 아니면 404를 반환합니다.
nexia.locale없음현지화된 App Route인증된 사용자의 요청 유효 locale을 해석합니다. 인증 뒤에 배치하며 표시 언어만 선택하고 권한은 부여하지 않습니다.
nexia.context.legal_entity고정 legalEntity Route 인자App·Core Route해석한 내부 ID를 request attribute legal_entity_id에 둡니다. 선택과 membership은 권한이 아닌 컨텍스트입니다.
nexia.context.workspace미들웨어 인자 없음App·Core 브라우저 Route사용자가 접근할 수 있을 때만 세션 workspace_id를 유지합니다.
can.tenant_wide:{permission}필수 권한 slugApp·Core 연산빈 테넌트 전체 데이터 대상을 기준으로 권한을 평가합니다. 더 좁은 Legal Entity·Operating Unit Grant로는 통과하지 못합니다.
can.scope:{permission},{scope_type},{route_parameter}[,{subject_population}]권한, 차원 키, Route 인자, self 같은 선택적 SDK population 값App·Core 연산명시적인 공개 Resource 대상을 해석하고 Access Grant를 평가하며, 해당하면 Legal Entity·Operating Unit request attribute를 기록합니다.
operating_unit.affiliated[:{legalEntityParameter},{operatingUnitParameter}]기본값 legalEntity, operatingUnit중첩된 App·Core Route해석된 두 Route Resource의 affiliation을 증명합니다. 이 구조 검사는 Grant를 만들지 않습니다.
nexia.context.operating_unit고정 operatingUnit Route 인자명시적 Operating Unit을 가진 App·Core Routerequest attribute operating_unit_id를 기록합니다. 위임 요청은 서명된 대상과 일치해야 합니다. SDK에 Middleware 상수가 없으므로 App Route는 현재 호스트 등록 문자열을 직접 사용합니다.
human.only없음사람 셀프서비스 App·Core Route위임 Agent 표식을 거부하고 인증된 사람 경로만 계속 실행합니다.
service-tokenX-Service-Token header, 미들웨어 인자 없음Core /internal/* 전용설정된 service secret으로 Agent gateway를 인증합니다. App Route 계약이 아닙니다.
agent_data:{action}기본값 read, *이면 요청에서 action을 읽음Core 범용 Agent-data endpoint 전용런타임 Resource 키를 해석하고 위임 allowlist와 실시간 권한을 강제합니다. App은 Resource를 contribution하지만 이 별칭을 붙이지 않습니다.
signature.enabled없음Core Signature 진입점 전용테넌트 기능이 꺼져 있으면 플랫폼 Signature 표면을 거부합니다. App Route 계약이 아닙니다.
tenant.require_2fa없음Tenant Shell Route 전용등록 화면을 위해 document Shell은 열어 두고, 아직 등록이 필요한 JSON 요청에는 423을 반환합니다.

옵션: guard 선택과 순서

호출하는 화면이 아니라 실제 연산의 데이터 소유자를 기준으로 capability guard를 고릅니다.

상황바깥 stack 다음에 추가이유
테넌트 소유 Catalog·Settingcan.tenant_wide:quality.inspection_method.read더 좁은 Resource 좌표가 없으므로 테넌트 전체 Grant만 적용됩니다.
URL에 Legal Entity 대상이 있는 경로can.scope:quality.inspection_result.read,core.legal_entity,legalEntityURL이 Access Grant 판단에 쓸 정확한 Legal Entity 대상을 제공합니다.
Operating Unit 소유 레코드operating_unit.affiliated 다음 can.scope:{permission},core.operating_unit,operatingUnitaffiliation은 Route 구조를, scoped guard는 권한을 별도로 증명하며 Legal Entity 좌표도 요구합니다.
Agent가 절대 호출하면 안 되는 셀프서비스 데이터capability guard보다 먼저 human.only사람 전용 조건은 호출자 종류를 좁힐 뿐 권한을 대신하지 않습니다.
한 population으로 제한된 권한self 같은 네 번째 can.scope 인자동작과 데이터 범위뿐 아니라 그 population도 결정에서 허용되어야 합니다.

app.installed는 개별 CRUD Route 바깥에 두어 App endpoint 전체가 같은 방식으로 닫히게 합니다. can.*는 가장 작은 구체 연산에 둡니다. Policy는 로드된 레코드를 다시 검사하고, 목록 술어는 범위 밖 행을 SQL에서 제외하며, 도메인 서비스는 전이 규칙을 강제합니다. 미들웨어는 어느 층도 대신하지 않습니다.

결과 또는 반환

통과한 미들웨어는 다음 응답을 반환합니다. 컨텍스트 미들웨어는 downstream 계약에 요청 로컬 값을 추가할 수 있지만, 그 값 자체는 권한의 증거가 아닙니다.

효과소비자보안 의미
활성 테넌트와 tenant database connectionModel, cache, filesystem, URL 생성App 코드보다 먼저 격리 경계가 확립됨
인증된 사람 또는 위임된 AgentController와 권한 서비스행위자 신원일 뿐임
검증된 브라우저 password session세션 검사가 붙은 Core API 루트 경로Password 변경으로 무효가 된 session은 거부하며 stateless 위임 요청은 이 호스트 검사를 그대로 통과
legal_entity_id request attribute권한 컨텍스트와 downstream 서비스명시적·선택 컨텍스트이며 Grant가 아님
operating_unit_id request attributeOperating Unit 인식 권한·서비스명시적 Route·위임 컨텍스트이며 affiliation이나 권한이 아님
세션 workspace_idShell과 브라우저 화면 복원화면 컨텍스트일 뿐임
can.* 권한 판단현재 연산거친 capability 판단이며 레코드·상태 검사는 이후에도 실행됨

생성된 App 경로는 아래 순서로 처리합니다. 인가는 경로 guard 또는 Controller에서 수행합니다.

tenant 식별
  -> tenant 격리 session
  -> 사람 또는 Agent 인증
  -> 위임 권한 축소
  -> App operational 검사
  -> request context 설정
  -> Route capability guard
  -> Policy와 목록 술어
  -> 도메인 전이

Core의 routes/api.php 루트 그룹에는 권한 거부 감사, throttle:api, 비밀번호 세션 검사가 명시적으로 붙습니다. 패키지 경로는 자체 provider에서 로드하므로 URL이 /api로 시작해도 이 그룹을 상속하지 않습니다. 필요한 rate limit은 경로에 명시하세요. 공유 api limiter는 일반 traffic을 tenant와 user id 또는 IP로 묶어 분당 300회 허용합니다. 위임 Agent traffic은 tenant와 Agent id로 묶은 별도 분당 240회 bucket을 사용하므로 사용자 browser bucket을 소모하지 않습니다. 더 엄격한 endpoint별 limit은 그 위에 계속 적용됩니다. API root는 stateful web guard로 Laravel password-hash session 검사도 실행하며, stateless 위임 traffic은 그대로 통과합니다. web과 api 그룹은 교환된 support session도 매 요청마다 다시 확인합니다. 이 자동 호스트 concern은 App이 패키지 Route 파일로 복사하는 별칭이 아닙니다.

오류

관찰 결과원인해결
401인증 실패, password 변경으로 브라우저 session 무효화, 또는 플랫폼 service-token 부재·불일치지원되는 사람·Agent 자격으로 다시 인증합니다. App에서 내부 service token을 사용하지 마세요.
agent.delegation의 403권한 slug, 서명된 조직 대상, 도구 정책이 위임 범위 밖올바르게 좁힌 토큰을 만들고 Route의 can.* 선언을 실제 capability와 맞춥니다.
can.tenant_wide 또는 can.scope의 403적용 가능한 Access Grant, 대상 차원, 요청 population이 없음Grant 또는 Route의 실제 데이터 좌표를 고칩니다. Shell 선택으로 대체하지 마세요.
human.only의 403위임 Agent가 사람 셀프서비스 endpoint 호출명시적 권한이 있는 Agent 가능 연산을 사용하거나 endpoint를 사람 전용으로 유지합니다.
app.installed의 404App이 없거나 disabled·미초기화·unknown 상태이거나 prerequisite가 막힘App closure를 설치·복구합니다. guard를 우회하지 마세요.
operating_unit.affiliated의 404Route의 Legal Entity와 Operating Unit이 affiliated 상태가 아님두 안정 공개 Route 식별자를 바로잡습니다.
419교환된 support session이 만료되었거나 중앙에서 회수됨가장 세션을 끝내고 승인된 support session을 새로 시작합니다.
JSON 423테넌트 정책상 현재 비밀번호 세션에 2FA 등록 필요등록을 완료합니다. SSO 정책은 외부 IdP가 계속 소유합니다.
429공유 API bucket 또는 더 엄격한 endpoint별 limiter 소진응답 rate-limit header를 따르고 backoff합니다. 호스트 limiter를 우회하지 마세요.
service-token의 503플랫폼 service-token 설정이 비어 있음내부 gateway 양쪽을 설정합니다. App fallback이 아니라 운영 문제입니다.

실제 사용

packages/quality/routes/routes.php는 테넌트 초기화, 인증, App 설치, Legal Entity 문맥, Workspace 문맥을 처리하는 생성된 외부 그룹을 유지합니다. Resource 경로를 추가할 때 동작에 맞는 권한 가드를 붙이세요. packages/people/routes/routes.php는 URL이 두 조직 좌표를 운반하는 곳에 nexia.context.operating_unit과 operating_unit.affiliated를 추가합니다. packages/payroll/routes/routes.php는 개인 급여 명세에 human.only와 네 번째 인자가 self인 can.scope를 함께 적용합니다.

상태를 바꾸지 않고 등록된 chain을 확인할 수 있습니다.

코드 예시
Shell
task artisan -- route:list --path=quality
task artisan -- route:list --path=legal-entities --json

집중 동작 근거는 tests/Feature/AppRuntime/TenantAppInstallationRuntimeTest.php, tests/Feature/Authorization/CanTenantWideMiddlewareTest.php, tests/Feature/Access/RouteOperatingUnitContextTest.php, tests/Feature/Agent/AgentDelegationGuardTest.php, tests/Feature/WorkspaceContextTest.php에 있습니다.

관련 문서

원본 위치: docs/developers/content/ko/app-sdk/http-middleware.md