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 @@...@@ -2,7 +2,6 @@
22
3const std = @import("std");3const std = @import("std");
4const string = []const u8;4const string = []const u8;
5const http = @import("apple_pie");
6const files = @import("self/files");5const files = @import("self/files");
7const pek = @import("pek");6const pek = @import("pek");
8const zfetch = @import("zfetch");7const zfetch = @import("zfetch");
...@@ -214,35 +213,28 @@ pub fn clientByProviderId(clients: []const Client, name: string) ?Client {...@@ -214,35 +213,28 @@ pub fn clientByProviderId(clients: []const Client, name: string) ?Client {
214 return null;213 return null;
215}214}
216215
217pub const IsLoggedInFn = fn (http.Request) anyerror!bool;216pub const IsLoggedInFn = fn (*std.http.Server.Response) anyerror!bool;
218217
219pub fn Handlers(comptime T: type) type {218pub fn Handlers(comptime T: type) type {
220 comptime std.debug.assert(@hasDecl(T, "Ctx"));
221 comptime std.debug.assert(@hasDecl(T, "isLoggedIn"));219 comptime std.debug.assert(@hasDecl(T, "isLoggedIn"));
222 comptime std.debug.assert(@hasDecl(T, "doneUrl"));220 comptime std.debug.assert(@hasDecl(T, "doneUrl"));
223 comptime std.debug.assert(@hasDecl(T, "saveInfo"));221 comptime std.debug.assert(@hasDecl(T, "saveInfo"));
222 comptime std.debug.assert(@hasDecl(T, "callbackPath"));
224223
225 return struct {224 return struct {
226 const Self = @This();225 const Self = @This();
227 pub var clients: []Client = &.{};226 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 {
236 if (query.get("with")) |with| {229 if (query.get("with")) |with| {
237 const client = clientByProviderId(Self.clients, with) orelse return try fail(response, "Client with that ID not found!\n", .{});230 const client = clientByProviderId(Self.clients, with) orelse return try fail(response, body_writer, "Client with that ID not found!\n", .{});
238 return try loginOne(response, request, T, client, Self.callbackPath);231 return try loginOne(response, alloc, T, client, T.callbackPath);
239 }232 }
240
241 if (Self.clients.len == 1) {233 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);
243 }235 }
244236
245 try response.headers.put("Content-Type", "text/html");237 try response.headers.append("Content-Type", "text/html");
246 const page = files.@"/selector.pek";238 const page = files.@"/selector.pek";
247 const tmpl = comptime pek.parse(page);239 const tmpl = comptime pek.parse(page);
248 try pek.compile(Base, alloc, response.writer(), tmpl, .{240 try pek.compile(Base, alloc, response.writer(), tmpl, .{
...@@ -250,22 +242,17 @@ pub fn Handlers(comptime T: type) type {...@@ -250,22 +242,17 @@ pub fn Handlers(comptime T: type) type {
250 });242 });
251 }243 }
252244
253 pub fn callback(_: T.Ctx, response: *http.Response, request: http.Request, captures: ?*const anyopaque) !void {245 pub fn callback(response: *std.http.Server.Response, body_writer: anytype, alloc: std.mem.Allocator, query: UrlValues) !void {
254 std.debug.assert(captures == null);246 const state = query.get("state") orelse return try fail(response, body_writer, "", .{});
255247 const client = clientByProviderId(Self.clients, state) orelse return try fail(response, body_writer, "error: No handler found for provider: {s}\n", .{state});
256 const alloc = request.arena;248 const code = query.get("code") orelse return try fail(response, body_writer, "", .{});
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, "", .{});
262249
263 var params = UrlValues.init(alloc);250 var params = UrlValues.init(alloc);
264 try params.add("client_id", client.id);251 try params.add("client_id", client.id);
265 try params.add("client_secret", client.secret);252 try params.add("client_secret", client.secret);
266 try params.add("grant_type", "authorization_code");253 try params.add("grant_type", "authorization_code");
267 try params.add("code", code);254 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));
269 try params.add("state", "none");256 try params.add("state", "none");
270257
271 const req = try zfetch.Request.init(alloc, client.provider.token_url, null);258 const req = try zfetch.Request.init(alloc, client.provider.token_url, null);
...@@ -298,49 +285,49 @@ pub fn Handlers(comptime T: type) type {...@@ -298,49 +285,49 @@ pub fn Handlers(comptime T: type) type {
298 const name = val2.root.object.get(client.provider.name_prop).?.string;285 const name = val2.root.object.get(client.provider.name_prop).?.string;
299 try T.saveInfo(response, alloc, client.provider, id, name, val.root, val2.root);286 try T.saveInfo(response, alloc, client.provider, id, name, val.root, val2.root);
300287
301 try response.headers.put("Location", T.doneUrl);288 try response.headers.append("Location", T.doneUrl);
302 try response.writeHeader(.found);289 response.status = .found;
303 }290 }
304 };291 };
305}292}
306293
307fn loginOne(response: *http.Response, request: http.Request, comptime T: type, client: Client, callbackPath: string) !void {294fn loginOne(response: *std.http.Server.Response, alloc: std.mem.Allocator, comptime T: type, client: Client, callbackPath: string) !void {
308 if (try T.isLoggedIn(request)) {295 if (try T.isLoggedIn(response, alloc)) {
309 try response.headers.put("Location", T.doneUrl);296 try response.headers.append("Location", T.doneUrl);
310 } else {297 } else {
311 const alloc = request.arena;
312 const idp = client.provider;298 const idp = client.provider;
313 var params = UrlValues.init(alloc);299 var params = UrlValues.init(alloc);
314 try params.add("client_id", client.id);300 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));
316 try params.add("response_type", "code");302 try params.add("response_type", "code");
317 try params.add("scope", idp.scope);303 try params.add("scope", idp.scope);
318 try params.add("duration", "temporary");304 try params.add("duration", "temporary");
319 try params.add("state", idp.id);305 try params.add("state", idp.id);
320 const authurl = try std.mem.join(alloc, "?", &.{ idp.authorize_url, try params.encode() });306 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);
322 }308 }
323 try response.writeHeader(.found);309 response.status = .found;
324}310}
325311
326fn fail(response: *http.Response, comptime err: string, args: anytype) !void {312fn fail(response: *std.http.Server.Response, body_writer: anytype, comptime err: string, args: anytype) !void {
327 try response.writeHeader(.bad_request);313 response.status = .bad_request;
328 try response.writer().print(err, args);314 try body_writer.print(err, args);
329}315}
330316
331fn redirectUri(alloc: std.mem.Allocator, request: http.Request, callbackPath: string) !string {317fn redirectUri(response: *std.http.Server.Response, alloc: std.mem.Allocator, callbackPath: string) !string {
332 const headers = try request.headers(alloc);318 const headers = response.request.headers;
333 const xproto = headers.get("X-Forwarded-Proto") orelse "";319 const xproto = headers.getFirstValue("X-Forwarded-Proto") orelse "";
334 const maybe_tls = std.mem.eql(u8, xproto, "https");320 const maybe_tls = std.mem.eql(u8, xproto, "https");
335 const proto: string = if (maybe_tls) "https" else "http";321 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 });
337}324}
338325
339fn fixId(alloc: std.mem.Allocator, id: json.Value) !string {326fn fixId(alloc: std.mem.Allocator, id: std.json.Value) !string {
340 return switch (id) {327 return switch (id) {
341 .String => |v| return v,328 .string => |v| v,
342 .Int => |v| return try std.fmt.allocPrint(alloc, "{d}", .{v}),329 .integer => |v| try std.fmt.allocPrint(alloc, "{d}", .{v}),
343 .Float => |v| return try std.fmt.allocPrint(alloc, "{d}", .{v}),330 .float => |v| try std.fmt.allocPrint(alloc, "{d}", .{v}),
344 else => unreachable,331 else => unreachable,
345 };332 };
346}333}
zig.mod-1
...@@ -6,7 +6,6 @@ description: HTTP handler functions to allow you to easily add OAuth2 login supp...@@ -6,7 +6,6 @@ description: HTTP handler functions to allow you to easily add OAuth2 login supp
6files:6files:
7 - www7 - www
8dependencies:8dependencies:
9 - src: git https://github.com/Luukdegram/apple_pie
10 - src: git https://github.com/nektro/zig-pek9 - src: git https://github.com/nektro/zig-pek
11 - src: git https://github.com/truemedian/zfetch10 - src: git https://github.com/truemedian/zfetch
12 - src: git https://github.com/nektro/zig-extras11 - src: git https://github.com/nektro/zig-extras