authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2023-05-31 01:24:20 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2023-05-31 01:24:20 -07:00
log27c2b45b88e1c90b4048506e9ab915b9440d625a
treeefb1690deed301b254b90f23abfab0791973abce
parentfc6019f80f5d9a4e4465f1f8d7978c252d9f23fa

use std.http instead of apple_pie


2 files changed, 32 insertions(+), 46 deletions(-)

src/lib.zig+32-45
......@@ -2,7 +2,6 @@
22
33const std = @import("std");
44const string = []const u8;
5const http = @import("apple_pie");
65const files = @import("self/files");
76const pek = @import("pek");
87const zfetch = @import("zfetch");
......@@ -214,35 +213,28 @@ pub fn clientByProviderId(clients: []const Client, name: string) ?Client {
214213 return null;
215214}
216215
217pub const IsLoggedInFn = fn (http.Request) anyerror!bool;
216pub const IsLoggedInFn = fn (*std.http.Server.Response) anyerror!bool;
218217
219218pub fn Handlers(comptime T: type) type {
220 comptime std.debug.assert(@hasDecl(T, "Ctx"));
221219 comptime std.debug.assert(@hasDecl(T, "isLoggedIn"));
222220 comptime std.debug.assert(@hasDecl(T, "doneUrl"));
223221 comptime std.debug.assert(@hasDecl(T, "saveInfo"));
222 comptime std.debug.assert(@hasDecl(T, "callbackPath"));
224223
225224 return struct {
226225 const Self = @This();
227226 pub var clients: []Client = &.{};
228 pub var callbackPath: string = "";
229
230 pub fn login(_: T.Ctx, response: *http.Response, request: http.Request, captures: ?*const anyopaque) !void {
231 std.debug.assert(captures == null);
232
233 const alloc = request.arena;
234 const query = try request.context.uri.queryParameters(alloc);
235227
228 pub fn login(response: *std.http.Server.Response, body_writer: anytype, alloc: std.mem.Allocator, query: UrlValues) !void {
236229 if (query.get("with")) |with| {
237 const client = clientByProviderId(Self.clients, with) orelse return try fail(response, "Client with that ID not found!\n", .{});
238 return try loginOne(response, request, T, client, Self.callbackPath);
230 const client = clientByProviderId(Self.clients, with) orelse return try fail(response, body_writer, "Client with that ID not found!\n", .{});
231 return try loginOne(response, alloc, T, client, T.callbackPath);
239232 }
240
241233 if (Self.clients.len == 1) {
242 return try loginOne(response, request, T, clients[0], Self.callbackPath);
234 return try loginOne(response, alloc, T, clients[0], T.callbackPath);
243235 }
244236
245 try response.headers.put("Content-Type", "text/html");
237 try response.headers.append("Content-Type", "text/html");
246238 const page = files.@"/selector.pek";
247239 const tmpl = comptime pek.parse(page);
248240 try pek.compile(Base, alloc, response.writer(), tmpl, .{
......@@ -250,22 +242,17 @@ pub fn Handlers(comptime T: type) type {
250242 });
251243 }
252244
253 pub fn callback(_: T.Ctx, response: *http.Response, request: http.Request, captures: ?*const anyopaque) !void {
254 std.debug.assert(captures == null);
255
256 const alloc = request.arena;
257 const query = try request.context.uri.queryParameters(alloc);
258
259 const state = query.get("state") orelse return try fail(response, "", .{});
260 const client = clientByProviderId(Self.clients, state) orelse return try fail(response, "error: No handler found for provider: {s}\n", .{state});
261 const code = query.get("code") orelse return try fail(response, "", .{});
245 pub fn callback(response: *std.http.Server.Response, body_writer: anytype, alloc: std.mem.Allocator, query: UrlValues) !void {
246 const state = query.get("state") orelse return try fail(response, body_writer, "", .{});
247 const client = clientByProviderId(Self.clients, state) orelse return try fail(response, body_writer, "error: No handler found for provider: {s}\n", .{state});
248 const code = query.get("code") orelse return try fail(response, body_writer, "", .{});
262249
263250 var params = UrlValues.init(alloc);
264251 try params.add("client_id", client.id);
265252 try params.add("client_secret", client.secret);
266253 try params.add("grant_type", "authorization_code");
267254 try params.add("code", code);
268 try params.add("redirect_uri", try redirectUri(alloc, request, callbackPath));
255 try params.add("redirect_uri", try redirectUri(response, alloc, T.callbackPath));
269256 try params.add("state", "none");
270257
271258 const req = try zfetch.Request.init(alloc, client.provider.token_url, null);
......@@ -298,49 +285,49 @@ pub fn Handlers(comptime T: type) type {
298285 const name = val2.root.object.get(client.provider.name_prop).?.string;
299286 try T.saveInfo(response, alloc, client.provider, id, name, val.root, val2.root);
300287
301 try response.headers.put("Location", T.doneUrl);
302 try response.writeHeader(.found);
288 try response.headers.append("Location", T.doneUrl);
289 response.status = .found;
303290 }
304291 };
305292}
306293
307fn loginOne(response: *http.Response, request: http.Request, comptime T: type, client: Client, callbackPath: string) !void {
308 if (try T.isLoggedIn(request)) {
309 try response.headers.put("Location", T.doneUrl);
294fn loginOne(response: *std.http.Server.Response, alloc: std.mem.Allocator, comptime T: type, client: Client, callbackPath: string) !void {
295 if (try T.isLoggedIn(response, alloc)) {
296 try response.headers.append("Location", T.doneUrl);
310297 } else {
311 const alloc = request.arena;
312298 const idp = client.provider;
313299 var params = UrlValues.init(alloc);
314300 try params.add("client_id", client.id);
315 try params.add("redirect_uri", try redirectUri(alloc, request, callbackPath));
301 try params.add("redirect_uri", try redirectUri(response, alloc, callbackPath));
316302 try params.add("response_type", "code");
317303 try params.add("scope", idp.scope);
318304 try params.add("duration", "temporary");
319305 try params.add("state", idp.id);
320306 const authurl = try std.mem.join(alloc, "?", &.{ idp.authorize_url, try params.encode() });
321 try response.headers.put("Location", authurl);
307 try response.headers.append("Location", authurl);
322308 }
323 try response.writeHeader(.found);
309 response.status = .found;
324310}
325311
326fn fail(response: *http.Response, comptime err: string, args: anytype) !void {
327 try response.writeHeader(.bad_request);
328 try response.writer().print(err, args);
312fn fail(response: *std.http.Server.Response, body_writer: anytype, comptime err: string, args: anytype) !void {
313 response.status = .bad_request;
314 try body_writer.print(err, args);
329315}
330316
331fn redirectUri(alloc: std.mem.Allocator, request: http.Request, callbackPath: string) !string {
332 const headers = try request.headers(alloc);
333 const xproto = headers.get("X-Forwarded-Proto") orelse "";
317fn redirectUri(response: *std.http.Server.Response, alloc: std.mem.Allocator, callbackPath: string) !string {
318 const headers = response.request.headers;
319 const xproto = headers.getFirstValue("X-Forwarded-Proto") orelse "";
334320 const maybe_tls = std.mem.eql(u8, xproto, "https");
335321 const proto: string = if (maybe_tls) "https" else "http";
336 return try std.fmt.allocPrint(alloc, "{s}://{s}{s}", .{ proto, request.host().?, callbackPath });
322 const host = response.request.headers.getFirstValue("host").?;
323 return try std.fmt.allocPrint(alloc, "{s}://{s}{s}", .{ proto, host, callbackPath });
337324}
338325
339fn fixId(alloc: std.mem.Allocator, id: json.Value) !string {
326fn fixId(alloc: std.mem.Allocator, id: std.json.Value) !string {
340327 return switch (id) {
341 .String => |v| return v,
342 .Int => |v| return try std.fmt.allocPrint(alloc, "{d}", .{v}),
343 .Float => |v| return try std.fmt.allocPrint(alloc, "{d}", .{v}),
328 .string => |v| v,
329 .integer => |v| try std.fmt.allocPrint(alloc, "{d}", .{v}),
330 .float => |v| try std.fmt.allocPrint(alloc, "{d}", .{v}),
344331 else => unreachable,
345332 };
346333}
zig.mod-1
......@@ -6,7 +6,6 @@ description: HTTP handler functions to allow you to easily add OAuth2 login supp
66files:
77 - www
88dependencies:
9 - src: git https://github.com/Luukdegram/apple_pie
109 - src: git https://github.com/nektro/zig-pek
1110 - src: git https://github.com/truemedian/zfetch
1211 - src: git https://github.com/nektro/zig-extras