Loading...
Loading...
Explain and apply Freetool's OpenFGA integration using onion/hexagonal architecture boundaries, including exactly where authorization logic belongs and where it must not be implemented. Use when reviewing auth design, adding permissions, changing OpenFGA tuple writes/checks, or teaching team conventions with real code samples.
npx skill4agent add c10r/freetool freetool-openfga-hexagonal-architecturesrc/Freetool.Application/src/Interfaces/IAuthorizationService.fssrc/Freetool.Infrastructure/src/Services/OpenFgaService.fssrc/Freetool.Api/src/Program.fssrc/Freetool.Api/src/Controllers/AuthenticatedControllerBase.fssrc/Freetool.Api/src/Controllers/SpaceController.fssrc/Freetool.Application/src/Handlers/SpaceHandler.fssrc/Freetool.Api/src/Services/IdentityProvisioningService.fsAPI layer (inbound adapters)UserIdApplication layer (ports + use cases)IAuthorizationServiceAuthSubjectAuthRelationAuthObjectInfrastructure layer (outbound adapter)OpenFgaServiceDomain layerIAuthorizationServicesrc/Freetool.Application/src/Interfaces/IAuthorizationService.fsOpenFgaServicesrc/Freetool.Infrastructure/src/Services/OpenFgaService.fsProgram.fsIAuthorizationServicesrc/Freetool.Application/src/Interfaces/IAuthorizationService.fstype IAuthorizationService =
abstract member CreateRelationshipsAsync: RelationshipTuple list -> Task<unit>
abstract member CheckPermissionAsync:
subject: AuthSubject -> relation: AuthRelation -> object: AuthObject -> Task<bool>src/Freetool.Infrastructure/src/Services/OpenFgaService.fsopen OpenFga.Sdk.Client
type OpenFgaService(apiUrl: string, logger: ILogger<OpenFgaService>, ?storeId: string) =
interface IAuthorizationService with
member _.CheckPermissionAsync(subject, relation, object) : Task<bool> =
task {
use client = createClient ()
let body =
ClientCheckRequest(
User = AuthTypes.subjectToString subject,
Relation = AuthTypes.relationToString relation,
Object = AuthTypes.objectToString object
)
let! response = client.Check(body)
return response.Allowed.GetValueOrDefault(false)
}src/Freetool.Api/src/Program.fsbuilder.Services.AddScoped<IAuthorizationService>(fun serviceProvider ->
let loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>()
let logger = loggerFactory.CreateLogger<OpenFgaService>()
OpenFgaService(openFgaApiUrl, logger, actualStoreId) :> IAuthorizationService)
|> ignoresrc/Freetool.Api/src/Controllers/AppController.fslet! hasPermission =
authorizationService.CheckPermissionAsync
(User(userId.Value.ToString()))
permission
(SpaceObject(spaceId.Value.ToString()))src/Freetool.Application/src/Handlers/SpaceHandler.fsif not (List.isEmpty tuplesToAdd) || not (List.isEmpty tuplesToRemove) then
do!
authService.UpdateRelationshipsAsync
{ TuplesToAdd = tuplesToAdd
TuplesToRemove = tuplesToRemove }HttpContext.Items["UserId"]AuthenticatedControllerBaseCurrentUserIdIAuthorizationService.CheckPermissionAsyncOpenFgaService403AuthRelationIAuthorizationService.fsAuthTypesOpenFgaService.WriteAuthorizationModelAsyncIAuthorizationServiceopen OpenFga.Sdk.*"create_app"AuthSubject/AuthRelation/AuthObjectrg -n "open OpenFga\\.Sdk|OpenFgaService|IAuthorizationService|CheckPermissionAsync|CreateRelationshipsAsync|UpdateRelationshipsAsync" src --glob '*.fs'rg -n "CurrentUserId|HttpContext\\.Items\\[\\\"UserId\\\"\\]|UseMiddleware<IapAuthMiddleware>|UseMiddleware<DevAuthMiddleware>" src/Freetool.Api/src --glob '*.fs'