summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwikiapiserver2026-06-25 12:45:50 +0200
committerwikiapiserver2026-06-25 12:45:50 +0200
commit742cd195c0018bcbc6e748d9100b643ffe1f6358 (patch)
tree91d674f549efdb99a341e15d20b07515386e3d01
parentad6b1f3138cd3cd953f9caa6ab5483f0d1ac03eb (diff)
downloadwikiapiserver-742cd195c0018bcbc6e748d9100b643ffe1f6358.tar.gz
feat: integrate Wikimedia Enterprise auth API on register
Register now calls POST /v1/login on the Wikimedia auth endpoint to obtain refresh_token and access_token. Tokens are hashed (SHA-256) before storage. If the API call fails, registration fails.
-rw-r--r--db/db.go64
1 files changed, 58 insertions, 6 deletions
diff --git a/db/db.go b/db/db.go
index 4439be4..7c26c8c 100644
--- a/db/db.go
+++ b/db/db.go
@@ -6,8 +6,12 @@ import (
"crypto/sha256"
"database/sql"
"encoding/hex"
+ "encoding/json"
"errors"
"fmt"
+ "io"
+ "net/http"
+ "net/url"
"strings"
"time"
@@ -74,6 +78,45 @@ func sha256hex(s string) string {
return hex.EncodeToString(h[:])
}
+// WikimediaTokens holds the tokens returned by the Wikimedia auth API.
+type WikimediaTokens struct {
+ RefreshToken string `json:"refresh_token"`
+ AccessToken string `json:"access_token"`
+}
+
+// Wikimedialogin sends credentials to the Wikimedia Enterprise auth API
+// and returns the refresh and access tokens.
+func Wikimedialogin(ctx context.Context, username, password string) (*WikimediaTokens, error) {
+ body := fmt.Sprintf("username=%s&password=%s",
+ url.PathEscape(username), url.PathEscape(password))
+
+ req, err := http.NewRequestWithContext(ctx, "POST",
+ "https://auth.enterprise.wikimedia.com/v1/login",
+ strings.NewReader(body))
+ if err != nil {
+ return nil, fmt.Errorf("new request: %w", err)
+ }
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return nil, fmt.Errorf("wikimedia login request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+ bs, _ := io.ReadAll(resp.Body)
+ return nil, fmt.Errorf("wikimedia login failed (status %d): %s", resp.StatusCode, string(bs))
+ }
+
+ var tokens WikimediaTokens
+ if err := json.NewDecoder(resp.Body).Decode(&tokens); err != nil {
+ return nil, fmt.Errorf("decode wikimedia tokens: %w", err)
+ }
+
+ return &tokens, nil
+}
+
// --- error helpers ---
func isDupKeyError(err error) bool {
@@ -82,13 +125,22 @@ func isDupKeyError(err error) bool {
// --- queries ---
-// CreateAccount inserts a new account with username and plaintext password.
-// Tokens are not generated here; they are set later via the Wikimedia API.
+// CreateAccount registers the user via the Wikimedia auth API,
+// then persists the account and tokens to the database.
+// If the Wikimedia API call fails, registration fails.
func (d *DB) CreateAccount(ctx context.Context, username, plaintextPW string) (*Account, error) {
+ // Obtain tokens from Wikimedia Enterprise auth API
+ tokens, err := Wikimedialogin(ctx, username, plaintextPW)
+ if err != nil {
+ return nil, fmt.Errorf("wikimedia login: %w", err)
+ }
+
res, err := d.conn.ExecContext(ctx,
`INSERT INTO account (username, password, refresh_token, access_token, access_token_created)
- VALUES (?, ?, '', '', NOW())`,
+ VALUES (?, ?, ?, ?, NOW())`,
username, plaintextPW,
+ sha256hex(tokens.RefreshToken),
+ sha256hex(tokens.AccessToken),
)
if err != nil {
if isDupKeyError(err) {
@@ -106,9 +158,9 @@ func (d *DB) CreateAccount(ctx context.Context, username, plaintextPW string) (*
return &Account{
ID: id,
Username: username,
- RefreshToken: "",
- AccessToken: "",
- AccessTokenExpiry: now,
+ RefreshToken: tokens.RefreshToken,
+ AccessToken: tokens.AccessToken,
+ AccessTokenExpiry: now.Add(accessTokenTTL),
CreatedAt: now,
}, nil
}