update t p

This commit is contained in:
2025-09-17 22:27:01 -03:00
parent a7a7cc6cc1
commit 0e6a503ee4
8 changed files with 51 additions and 17 deletions

View File

@@ -1,4 +1,3 @@
from http import HTTPStatus
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
import jwt import jwt
@@ -41,10 +40,9 @@ def authorize():
client_scopes = set(scope_to_list(grant.client.scope)) client_scopes = set(scope_to_list(grant.client.scope))
user_scopes = set(scope_to_list(session_scope)) if session_scope else set() user_scopes = set(scope_to_list(session_scope)) if session_scope else set()
# Deny authorization if user has no scopes matching the client request # Deny authorization if user lacks scopes requested by client
if not user_scopes & client_scopes: if not client_scopes.issubset(user_scopes):
raise ForbiddenError() raise ForbiddenError('Access denied')
# raise errors.InvalidScopeError(status_code=HTTPStatus.UNAUTHORIZED)
return server.create_authorization_response( return server.create_authorization_response(
request=router.current_event, request=router.current_event,

View File

@@ -2,4 +2,5 @@ export const OK = 200
export const FOUND = 302 export const FOUND = 302
export const BAD_REQUEST = 400 export const BAD_REQUEST = 400
export const UNAUTHORIZED = 401 export const UNAUTHORIZED = 401
export const FORBIDDEN = 403
export const INTERNAL_SERVER = 500 export const INTERNAL_SERVER = 500

View File

@@ -6,7 +6,10 @@ import {
} from '@react-router/dev/routes' } from '@react-router/dev/routes'
export default [ export default [
layout('routes/layout.tsx', [index('routes/index.tsx')]), layout('routes/layout.tsx', [
index('routes/index.tsx'),
route('/deny', 'routes/deny.tsx')
]),
route('/authorize', 'routes/authorize.ts'), route('/authorize', 'routes/authorize.ts'),
route('/token', 'routes/token.ts'), route('/token', 'routes/token.ts'),
route('/revoke', 'routes/revoke.ts') route('/revoke', 'routes/revoke.ts')

View File

@@ -30,6 +30,12 @@ export async function loader({ request, context }: Route.LoaderArgs) {
redirect: 'manual' redirect: 'manual'
}) })
console.log('Issuer response', {
json: await r.json(),
headers: r.headers,
status: r.status
})
if (r.status === httpStatus.FOUND) { if (r.status === httpStatus.FOUND) {
return new Response(await r.text(), { return new Response(await r.text(), {
status: r.status, status: r.status,
@@ -37,11 +43,15 @@ export async function loader({ request, context }: Route.LoaderArgs) {
}) })
} }
console.log('Issuer response', { // Deny authorization if user lacks scopes requested by client
json: await r.json(), if (r.status === httpStatus.FORBIDDEN) {
headers: r.headers, return new Response(null, {
status: r.status status: httpStatus.FOUND,
}) headers: {
Location: new URL('/deny', url.origin).toString()
}
})
}
return new Response(null, { return new Response(null, {
status: httpStatus.FOUND, status: httpStatus.FOUND,

View File

@@ -0,0 +1,20 @@
import { LockIcon } from 'lucide-react'
import type { Route } from './+types'
export function meta({}: Route.MetaArgs) {
return [{ title: 'Acesso negado' }]
}
export default function Deny({}: Route.ComponentProps) {
return (
<>
<div className="flex flex-col text-center items-center gap-6">
<LockIcon className="size-12" />
<div className="space-y-1.5">
<h1 className="text-xl text-gray-10 font-bold">Acesso negado.</h1>
<p>Você não tem permissão.</p>
</div>
</div>
</>
)
}

View File

@@ -8,8 +8,6 @@ export async function action({ request, context }: Route.ActionArgs) {
body: await request.text() body: await request.text()
}) })
// console.log(await r.text(), r)
return new Response(await r.text(), { return new Response(await r.text(), {
status: r.status, status: r.status,
headers: r.headers headers: r.headers

View File

@@ -53,14 +53,14 @@ def test_authorize(
assert len(r['items']) == 3 assert len(r['items']) == 3
def test_unauthorized( def test_forbidden(
app, app,
seeds, seeds,
dynamodb_persistence_layer: DynamoDBPersistenceLayer, dynamodb_persistence_layer: DynamoDBPersistenceLayer,
http_api_proxy: HttpApiProxy, http_api_proxy: HttpApiProxy,
lambda_context: LambdaContext, lambda_context: LambdaContext,
): ):
session_id = new_session(USER_ID) session_id = new_session('fd5914ec-fd37-458b-b6b9-8aeab38b666b')
r = app.lambda_handler( r = app.lambda_handler(
http_api_proxy( http_api_proxy(
@@ -81,7 +81,7 @@ def test_unauthorized(
lambda_context, lambda_context,
) )
assert r['statusCode'] == HTTPStatus.UNAUTHORIZED assert r['statusCode'] == HTTPStatus.FORBIDDEN
def test_authorize_revoked( def test_authorize_revoked(

View File

@@ -13,5 +13,9 @@
// User data // User data
{"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "0", "name": "Sérgio R Siqueira", "email": "sergio@somosbeta.com.br"} {"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "0", "name": "Sérgio R Siqueira", "email": "sergio@somosbeta.com.br"}
{"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "PASSWORD", "hash": "$pbkdf2-sha256$29000$IuTcm7M2BiAEgPB.b.3dGw$d8xVCbx8zxg7MeQBrOvCOgniiilsIHEMHzoH/OXftLQ"} {"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "PASSWORD", "hash": "$pbkdf2-sha256$29000$IuTcm7M2BiAEgPB.b.3dGw$d8xVCbx8zxg7MeQBrOvCOgniiilsIHEMHzoH/OXftLQ"}
{"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "SCOPE", "scope": "read:users read:enrollments"} {"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "SCOPE", "scope": "openid profile email offline_access read:users read:courses"}
{"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "SESSION#36af142e-9f6d-49d3-bfe9-6a6bd6ab2712", "created_at": "2025-09-17T13:44:34.544491-03:00", "ttl": 1760719474} {"id": "357db1c5-7442-4075-98a3-fbe5c938a419", "sk": "SESSION#36af142e-9f6d-49d3-bfe9-6a6bd6ab2712", "created_at": "2025-09-17T13:44:34.544491-03:00", "ttl": 1760719474}
{"id": "fd5914ec-fd37-458b-b6b9-8aeab38b666b", "sk": "0", "name": "Johnny Cash", "email": "johnny@johnnycash.com"}
{"id": "fd5914ec-fd37-458b-b6b9-8aeab38b666b", "sk": "PASSWORD", "hash": "$pbkdf2-sha256$29000$IuTcm7M2BiAEgPB.b.3dGw$d8xVCbx8zxg7MeQBrOvCOgniiilsIHEMHzoH/OXftLQ"}
{"id": "fd5914ec-fd37-458b-b6b9-8aeab38b666b", "sk": "SCOPE", "scope": "openid"}