summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim <contact@bytim.eu>2025-04-19 19:33:04 +0200
committerTim <contact@bytim.eu>2025-04-19 19:33:04 +0200
commit6a9657a10dc5ef3c4dfddf222284eec6c933ac83 (patch)
treea2b939a2e51ad04dd2635126be27d66c781666dd
parentff9b1e112ed14746ed74dfff1fb4c19efd5502d4 (diff)
downloadVPNAuth-6a9657a10dc5ef3c4dfddf222284eec6c933ac83.tar.xz
VPNAuth-6a9657a10dc5ef3c4dfddf222284eec6c933ac83.zip
Add OIDC user-information endpointHEADmaster
-rw-r--r--VPNAuth.Server/Database/AccessToken.cs1
-rw-r--r--VPNAuth.Server/Database/AuthRequest.cs1
-rw-r--r--VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.Designer.cs131
-rw-r--r--VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.cs40
-rw-r--r--VPNAuth.Server/Migrations/DatabaseModelSnapshot.cs8
-rw-r--r--VPNAuth.Server/Pages/Auth.cshtml.cs3
-rw-r--r--VPNAuth.Server/Pages/Dashboard.cshtml2
-rw-r--r--VPNAuth.Server/Program.cs78
8 files changed, 256 insertions, 8 deletions
diff --git a/VPNAuth.Server/Database/AccessToken.cs b/VPNAuth.Server/Database/AccessToken.cs
index 3cdc3ba..bb8fe7d 100644
--- a/VPNAuth.Server/Database/AccessToken.cs
+++ b/VPNAuth.Server/Database/AccessToken.cs
@@ -7,4 +7,5 @@ public class AccessToken
public string ClientId { get; set; }
public DateTime CreationTime { get; set; }
public List<string> Scopes { get; set; }
+ public string Username { get; set; }
}
diff --git a/VPNAuth.Server/Database/AuthRequest.cs b/VPNAuth.Server/Database/AuthRequest.cs
index 98fe001..11c05dc 100644
--- a/VPNAuth.Server/Database/AuthRequest.cs
+++ b/VPNAuth.Server/Database/AuthRequest.cs
@@ -11,4 +11,5 @@ public class AuthRequest
public string CodeChallenge { get; set; }
public string CodeChallengeMethod { get; set; }
public bool Accepted { get; set; }
+ public string Username { get; set; }
}
diff --git a/VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.Designer.cs b/VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.Designer.cs
new file mode 100644
index 0000000..8409c25
--- /dev/null
+++ b/VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.Designer.cs
@@ -0,0 +1,131 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using VPNAuth.Server.Database;
+
+#nullable disable
+
+namespace VPNAuth.Server.Migrations
+{
+ [DbContext(typeof(Database.Database))]
+ [Migration("20250419123149_AddUsernameFields")]
+ partial class AddUsernameFields
+ {
+ /// <inheritdoc />
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "9.0.4");
+
+ modelBuilder.Entity("VPNAuth.Server.Database.AccessToken", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ClientId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<DateTime>("CreationTime")
+ .HasColumnType("TEXT");
+
+ b.PrimitiveCollection<string>("Scopes")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Token")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("AccessTokens");
+ });
+
+ modelBuilder.Entity("VPNAuth.Server.Database.AuthRequest", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<bool>("Accepted")
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("ClientId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Code")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("CodeChallenge")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("CodeChallengeMethod")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<DateTime>("InitTime")
+ .HasColumnType("TEXT");
+
+ b.PrimitiveCollection<string>("Scopes")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property<string>("State")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("AuthRequests");
+ });
+
+ modelBuilder.Entity("VPNAuth.Server.Database.UserInformation", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property<string>("Email")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("FamilyName")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("GivenName")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Name")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Picture")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("PreferredUsername")
+ .HasColumnType("TEXT");
+
+ b.Property<string>("Sub")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("UserInformation");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.cs b/VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.cs
new file mode 100644
index 0000000..3d649bb
--- /dev/null
+++ b/VPNAuth.Server/Migrations/20250419123149_AddUsernameFields.cs
@@ -0,0 +1,40 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace VPNAuth.Server.Migrations
+{
+ /// <inheritdoc />
+ public partial class AddUsernameFields : Migration
+ {
+ /// <inheritdoc />
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn<string>(
+ name: "Username",
+ table: "AuthRequests",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: "");
+
+ migrationBuilder.AddColumn<string>(
+ name: "Username",
+ table: "AccessTokens",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: "");
+ }
+
+ /// <inheritdoc />
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Username",
+ table: "AuthRequests");
+
+ migrationBuilder.DropColumn(
+ name: "Username",
+ table: "AccessTokens");
+ }
+ }
+}
diff --git a/VPNAuth.Server/Migrations/DatabaseModelSnapshot.cs b/VPNAuth.Server/Migrations/DatabaseModelSnapshot.cs
index e4643df..4dcce6b 100644
--- a/VPNAuth.Server/Migrations/DatabaseModelSnapshot.cs
+++ b/VPNAuth.Server/Migrations/DatabaseModelSnapshot.cs
@@ -38,6 +38,10 @@ namespace VPNAuth.Server.Migrations
.IsRequired()
.HasColumnType("TEXT");
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
b.HasKey("Id");
b.ToTable("AccessTokens");
@@ -78,6 +82,10 @@ namespace VPNAuth.Server.Migrations
b.Property<string>("State")
.HasColumnType("TEXT");
+ b.Property<string>("Username")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
b.HasKey("Id");
b.ToTable("AuthRequests");
diff --git a/VPNAuth.Server/Pages/Auth.cshtml.cs b/VPNAuth.Server/Pages/Auth.cshtml.cs
index bdcbc59..1f75492 100644
--- a/VPNAuth.Server/Pages/Auth.cshtml.cs
+++ b/VPNAuth.Server/Pages/Auth.cshtml.cs
@@ -44,7 +44,8 @@ public class Auth : PageModel
Scopes = Request.Query["scope"].ToString().Split(" ").ToList(),
CodeChallenge = Request.Query["code_challenge"]!,
CodeChallengeMethod = Request.Query["code_challenge_method"]!,
- Accepted = false
+ Accepted = false,
+ Username = User!.Username!
});
db.SaveChanges();
}
diff --git a/VPNAuth.Server/Pages/Dashboard.cshtml b/VPNAuth.Server/Pages/Dashboard.cshtml
index 38f9c7e..78f6846 100644
--- a/VPNAuth.Server/Pages/Dashboard.cshtml
+++ b/VPNAuth.Server/Pages/Dashboard.cshtml
@@ -35,7 +35,7 @@
<h2>VPNAuth</h2>
<p>Hey, @configUser.Username!</p>
<h3>User settings</h3>
- <form hx-post="/user-info" hx-swap="none" hx-trigger="change">
+ <form hx-post="/user-info-settings" hx-swap="none" hx-trigger="change">
<table style="margin-left: auto; margin-right: auto;">
<tbody>
<tr>
diff --git a/VPNAuth.Server/Program.cs b/VPNAuth.Server/Program.cs
index 822aba7..beae428 100644
--- a/VPNAuth.Server/Program.cs
+++ b/VPNAuth.Server/Program.cs
@@ -36,6 +36,12 @@ app.MapGet("/accept-auth/{id}", async (HttpContext context, int id) =>
return;
}
+ if (authRequest.Username != context.GetUser()?.Username)
+ {
+ context.Response.StatusCode = StatusCodes.Status403Forbidden;
+ return;
+ }
+
authRequest.Accepted = true;
db.SaveChanges();
@@ -80,7 +86,8 @@ app.MapPost("/access-token", async (HttpContext context) =>
ClientId = authRequest.ClientId,
Scopes = authRequest.Scopes,
CreationTime = DateTime.Now,
- Token = PkceUtils.GenerateToken()
+ Token = PkceUtils.GenerateToken(),
+ Username = authRequest.Username
});
db.SaveChanges();
@@ -92,7 +99,7 @@ app.MapPost("/access-token", async (HttpContext context) =>
});
});
-app.MapPost("/user-info", async (HttpContext context) =>
+app.MapPost("/user-info-settings", async (HttpContext context) =>
{
using var db = new Database();
@@ -103,8 +110,10 @@ app.MapPost("/user-info", async (HttpContext context) =>
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
- UserInformation? userInformation = db.UserInformation.Where(user => user.Sub == configUser!.Username)
- .ToList().FirstOrDefault() ?? db.Add(new UserInformation
+ UserInformation? userInformation = db.UserInformation
+ .Where(user => user.Sub == configUser!.Username)
+ .ToList()
+ .FirstOrDefault() ?? db.Add(new UserInformation
{
Sub = configUser!.Username
}).Entity;
@@ -120,15 +129,72 @@ app.MapPost("/user-info", async (HttpContext context) =>
if (context.Request.Form.ContainsKey("email"))
userInformation.Email = context.Request.Form["email"]!;
-
+
if (context.Request.Form.ContainsKey("picture"))
userInformation.Picture = context.Request.Form["picture"]!;
-
+
userInformation.Name = userInformation.GivenName + " " + userInformation.FamilyName;
db.SaveChanges();
});
+app.Map("/user-info", (HttpContext context) =>
+{
+ if (context.Request.Method != "GET" && context.Request.Method != "POST")
+ {
+ context.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
+ return;
+ }
+
+ var tokenHeader = context.Request.Headers["Authorization"].First()?.Split(" ");
+
+ if (tokenHeader?.Length == 1 || tokenHeader?[0] != "Bearer")
+ {
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ return;
+ }
+
+ if (tokenHeader.Length >= 2)
+ {
+ context.Response.StatusCode = StatusCodes.Status401Unauthorized;
+ return;
+ }
+
+ using var db = new Database();
+ var tokenDbEntry = db.AccessTokens
+ .Where(tokenEntry => tokenEntry.Token == tokenHeader[1])
+ .ToList()
+ .FirstOrDefault();
+
+ if (tokenDbEntry == null)
+ {
+ context.Response.StatusCode = StatusCodes.Status403Forbidden;
+ return;
+ }
+
+ var userInformation = db.UserInformation
+ .Where(entry => entry.Sub == tokenDbEntry.Username)
+ .ToList()
+ .FirstOrDefault();
+
+ if (userInformation == null)
+ {
+ context.Response.StatusCode = StatusCodes.Status204NoContent;
+ return;
+ }
+
+ context.Response.WriteAsJsonAsync(new UserInfo
+ {
+ Email = userInformation.Email,
+ GivenName = userInformation.GivenName,
+ FamilyName = userInformation.FamilyName,
+ Name = userInformation.Name,
+ Picture = userInformation.Picture,
+ PreferredUsername = userInformation.PreferredUsername,
+ Sub = userInformation.Sub
+ });
+});
+
app.MapStaticAssets();
app.MapRazorPages()
.WithStaticAssets();