1
0
Fork 0
mirror of https://github.com/zigzap/zap.git synced 2025-10-20 07:04:08 +00:00
zap/examples/app/auth.zig
Tesseract22 c0cc025eda Provide defaults to unprovided restful method handler
Make `get`, `post`, ... methods optional. Check whether these method
exist at comptime. When no corresponding method is provided,
the handler simply return immediately.

Since it uses comptime, hopefully it should not add any checks at
runtime.
2025-07-13 15:02:35 +08:00

118 lines
3.7 KiB
Zig

//!
//! Part of the Zap examples.
//!
//! Build me with `zig build app_auth`.
//! Run me with `zig build run-app_auth`.
//!
const std = @import("std");
const zap = @import("zap");
const Allocator = std.mem.Allocator;
// The "Application Context"
const MyContext = struct {
bearer_token: []const u8,
};
// We reply with this
const HTTP_RESPONSE_TEMPLATE: []const u8 =
\\ <html><body>
\\ {s} from ZAP on {s} (token {s} == {s} : {s})!!!
\\ </body></html>
\\
;
// Our simple endpoint that will be wrapped by the authenticator
const MyEndpoint = struct {
// the slug
path: []const u8,
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
fn get_bearer_token(r: zap.Request) []const u8 {
const auth_header = zap.Auth.extractAuthHeader(.Bearer, &r) orelse "Bearer (no token)";
return auth_header[zap.Auth.AuthScheme.Bearer.str().len..];
}
// authenticated GET requests go here
// we use the endpoint, the context, the arena, and try
pub fn get(ep: *MyEndpoint, arena: Allocator, context: *MyContext, r: zap.Request) !void {
const used_token = get_bearer_token(r);
const response = try std.fmt.allocPrint(
arena,
HTTP_RESPONSE_TEMPLATE,
.{ "Hello", ep.path, used_token, context.bearer_token, "OK" },
);
r.setStatus(.ok);
try r.sendBody(response);
}
// we also catch the unauthorized callback
// we use the endpoint, the context, the arena, and try
pub fn unauthorized(ep: *MyEndpoint, arena: Allocator, context: *MyContext, r: zap.Request) !void {
r.setStatus(.unauthorized);
const used_token = get_bearer_token(r);
const response = try std.fmt.allocPrint(
arena,
HTTP_RESPONSE_TEMPLATE,
.{ "UNAUTHORIZED", ep.path, used_token, context.bearer_token, "NOT OK" },
);
try r.sendBody(response);
}
};
pub fn main() !void {
var gpa: std.heap.GeneralPurposeAllocator(.{
// just to be explicit
.thread_safe = true,
}) = .{};
defer std.debug.print("\n\nLeaks detected: {}\n\n", .{gpa.deinit() != .ok});
const allocator = gpa.allocator();
// our global app context
var my_context: MyContext = .{ .bearer_token = "ABCDEFG" }; // ABCDEFG is our Bearer token
// our global app that holds the context
// App is the type
// app is the instance
const App = zap.App.Create(MyContext);
try App.init(allocator, &my_context, .{});
defer App.deinit();
// create mini endpoint
var ep: MyEndpoint = .{
.path = "/test",
};
// create authenticator, use token from context
const Authenticator = zap.Auth.BearerSingle; // Simple Authenticator that uses a single bearer token
var authenticator = try Authenticator.init(allocator, my_context.bearer_token, null);
defer authenticator.deinit();
// create authenticating endpoint by combining endpoint and authenticator
const BearerAuthEndpoint = App.Endpoint.Authenticating(MyEndpoint, Authenticator);
var auth_ep = BearerAuthEndpoint.init(&ep, &authenticator);
// make the authenticating endpoint known to the app
try App.register(&auth_ep);
// listen
try App.listen(.{
.interface = "0.0.0.0",
.port = 3000,
});
std.debug.print(
\\ Run the following:
\\
\\ curl http://localhost:3000/test -i -H "Authorization: Bearer ABCDEFG" -v
\\ curl http://localhost:3000/test -i -H "Authorization: Bearer invalid" -v
\\
\\ and see what happens
\\
, .{});
// start worker threads
zap.start(.{
.threads = 2,
.workers = 1,
});
}