Skip to content

Document authentication

gisila_doc reads gisila’s guard annotations and documents what they enforce. In this guide you guard a controller, pick the security schemes that match your authenticator, and check that Swagger UI shows the lock icon and lets you authorize.

Use gisila’s guards on the class or method. @Public() on a method removes the class guards for that route.

import 'package:gisila_doc/gisila_doc.dart';
part 'admin_controller.g.dart';
@Controller('/admin', ['Admin'])
@RequireAuth()
class AdminController {
@Get('/health')
@Public()
Future<Map<String, String>> health() async => {'status': 'ok'};
@Get('/users')
@Permissions({'users:read'})
Future<List<User>> users(UserService users) => users.list();
@Delete('/users/{id}')
@Roles({'admin'})
@AnyPermission({'users:delete', 'users:manage'})
Future<void> deleteUser(String id, UserService users) => users.delete(id);
}

The guards run at request time through gisilaRoute, exactly as in plain gisila. For the document, each guarded operation gets:

  • security: one alternative per scheme in OpenApiSpec.securitySchemes, with the route’s roles and @Permissions values as the scope list;
  • a 401 response, and a 403 when roles or permissions apply, both with gisila’s error envelope (unless you declare those codes with @ApiResponse);
  • x-gisila-guards with the raw permissions, anyPermission and roles, because OpenAPI has no standard way to say “any of these permissions”.

/admin/health gets none of these.

The default is a single HTTP bearer scheme named bearerAuth, which matches gisila’s Authorization: Bearer authenticators. Change it to match how your authenticator reads credentials:

final spec = OpenApiSpec(
info: const ApiInfo(title: 'Admin API', version: '1.0.0'),
securitySchemes: const {
'bearerAuth': SecurityScheme.bearer(description: 'JWT from /auth/login'),
'apiKey': SecurityScheme.apiKey(
name: 'X-API-Key',
description: 'Server-to-server key',
),
},
);

Every scheme is listed as an alternative on every guarded route; per-route scheme selection is not supported. Pass securitySchemes: const {} to leave security out of the document entirely.

Schemes are emitted only when at least one operation is guarded.

final app = GisilaApp(
config: AppConfig(
authenticator: const StaticTokenAuthenticator({
'dev-admin-token': Principal(
id: 'admin',
roles: {'admin'},
permissions: {'users:read', 'users:delete'},
),
}),
),
);

StaticTokenAuthenticator is for demos and tests; use gisila’s JWT or API key authenticators in production.

SecurityScheme covers http and apiKey. For OAuth 2 or OpenID Connect, add the scheme through extra; it is merged into components.securitySchemes:

final spec = OpenApiSpec(
info: const ApiInfo(title: 'Admin API', version: '1.0.0'),
securitySchemes: const {'oidc': SecurityScheme(type: 'openIdConnect')},
extra: {
'components': {
'securitySchemes': {
'oidc': {
'type': 'openIdConnect',
'openIdConnectUrl': 'https://auth.example.com/.well-known/openid-configuration',
},
},
},
},
);

The securitySchemes entry makes guarded operations reference oidc; the extra entry, which wins on a name clash, supplies the full definition.

Terminal window
curl -s localhost:8080/openapi.json | jq '.paths["/admin/users"].get.security'
# [{"bearerAuth": ["users:read"]}, {"apiKey": ["users:read"]}]
curl -i localhost:8080/admin/users
# HTTP/1.1 401 ...
curl -s localhost:8080/admin/users -H 'authorization: Bearer dev-admin-token'

In Swagger UI, guarded operations show a lock. Click Authorize, enter the token, and “Try it out” sends it. persistAuthorization is on, so the token survives a page reload.