| ... | @@ -239,6 +239,59 @@ pub fn providerById(alloc: std.mem.Allocator, name: string) !?Provider { | ... | @@ -239,6 +239,59 @@ pub fn providerById(alloc: std.mem.Allocator, name: string) !?Provider { |
| 239 | }; | 239 | }; |
| 240 | } | 240 | } |
| 241 | } | 241 | } |
| | 242 | if (std.mem.eql(u8, p_id, "oidc")) { |
| | 243 | const io = root.io; |
| | 244 | var buf: [4096]u8 = @splat(0); |
| | 245 | var http_client: std.http.Client = .{ .allocator = alloc, .io = io }; |
| | 246 | defer http_client.deinit(); |
| | 247 | |
| | 248 | const url_s = try nio.fmt.allocPrint(alloc, "https://{s}/.well-known/openid-configuration", .{domain}); |
| | 249 | defer alloc.free(url_s); |
| | 250 | |
| | 251 | var req = try http_client.request(.GET, try std.Uri.parse(url_s), .{ |
| | 252 | .headers = .{ |
| | 253 | .accept_encoding = .{ .override = "identity" }, |
| | 254 | }, |
| | 255 | .redirect_behavior = .not_allowed, |
| | 256 | }); |
| | 257 | defer req.deinit(); |
| | 258 | try req.sendBodiless(); |
| | 259 | var resp = try req.receiveHead(&.{}); |
| | 260 | const body_content = try resp.reader(&buf).allocRemaining(alloc, .limited(1024 * 1024 * 5)); |
| | 261 | defer alloc.free(body_content); |
| | 262 | if (resp.head.status != .ok) std.log.scoped(.oauth).err("GET '{s}': {d}", .{ url_s, resp.head.status }); |
| | 263 | if (resp.head.status != .ok) std.log.scoped(.oauth).err("{s}", .{body_content}); |
| | 264 | if (resp.head.status != .ok) return null; |
| | 265 | const val = try json.parseFromSlice(alloc, "body.json", body_content, .{ .maximum_depth = 100, .support_trailing_commas = true }); |
| | 266 | defer val.deinit(alloc); |
| | 267 | val.acquire(); |
| | 268 | defer val.release(); |
| | 269 | const rootobj = val.root.object(); |
| | 270 | const authorize_url = rootobj.getS("authorization_endpoint") orelse { |
| | 271 | std.log.scoped(.oauth).err("openid-configuration did not have the expected 'authorization_endpoint' key", .{}); |
| | 272 | return null; |
| | 273 | }; |
| | 274 | const token_url = rootobj.getS("token_endpoint") orelse { |
| | 275 | std.log.scoped(.oauth).err("openid-configuration did not have the expected 'token_endpoint' key", .{}); |
| | 276 | return null; |
| | 277 | }; |
| | 278 | const me_url = rootobj.getS("userinfo_endpoint") orelse { |
| | 279 | std.log.scoped(.oauth).err("openid-configuration did not have the expected 'userinfo_endpoint' key", .{}); |
| | 280 | return null; |
| | 281 | }; |
| | 282 | return Provider{ |
| | 283 | .id = name, |
| | 284 | .authorize_url = try alloc.dupe(u8, authorize_url), |
| | 285 | .token_url = try alloc.dupe(u8, token_url), |
| | 286 | .me_url = try alloc.dupe(u8, me_url), |
| | 287 | .scope = "openid", |
| | 288 | .name_prop = "name", |
| | 289 | .name_prefix = "", |
| | 290 | .id_prop = "sub", |
| | 291 | .logo = icon_url("openid"), |
| | 292 | .color = "#F78C40", |
| | 293 | }; |
| | 294 | } |
| 242 | inline for (comptime extras.globalOption("oauth2_dynamic_providers", []const Provider) orelse &.{}) |didp| { | 295 | inline for (comptime extras.globalOption("oauth2_dynamic_providers", []const Provider) orelse &.{}) |didp| { |
| 243 | if (std.mem.eql(u8, didp.id, p_id)) { | 296 | if (std.mem.eql(u8, didp.id, p_id)) { |
| 244 | return Provider{ | 297 | return Provider{ |
| ... | @@ -329,7 +382,7 @@ pub fn Handlers(comptime T: type) type { | ... | @@ -329,7 +382,7 @@ pub fn Handlers(comptime T: type) type { |
| 329 | const val = try json.parseFromSlice(alloc, "body.json", body_content, .{ .maximum_depth = 100, .support_trailing_commas = true }); | 382 | const val = try json.parseFromSlice(alloc, "body.json", body_content, .{ .maximum_depth = 100, .support_trailing_commas = true }); |
| 330 | val.acquire(); | 383 | val.acquire(); |
| 331 | const tt = val.root.object().getS("token_type").?; | 384 | const tt = val.root.object().getS("token_type").?; |
| 332 | if (!std.mem.eql(u8, tt, "bearer")) return fail(response_status, body_writer, "oauth2: invalid token type: {s}", .{tt}); | 385 | if (!std.ascii.eqlIgnoreCase(tt, "bearer")) return fail(response_status, body_writer, "oauth2: invalid token type: expected 'bearer', got '{s}'", .{tt}); |
| 333 | const at = val.root.object().getS("access_token") orelse return try fail(response_status, body_writer, "Identity Provider Login Error!\n{s}", .{body_content}); | 386 | const at = val.root.object().getS("access_token") orelse return try fail(response_status, body_writer, "Identity Provider Login Error!\n{s}", .{body_content}); |
| 334 | val.release(); | 387 | val.release(); |
| 335 | | 388 | |