feat: login MAS OIDC end-to-end (issue 0147)
Backend Go: - MatrixService with Login/GetSession/Logout bindings - Uses 3 registry helpers via go.work: - mas_oidc_loopback_go_infra (PKCE flow) - keyring_token_store_go_infra (SO keychain) - matrix_client_init_go_infra (mautrix client) - whoami helper to discover user_id+device_id pre-init Frontend React+Vite+TS: - Mantine v7 + @tabler/icons-react - LoginScreen with 'Sign in with Matrix' button - HomeScreen with profile card + logout - Dark theme violet accent - @mantine/notifications wired Wails config: - Switched to pnpm (workspace: protocol) - Bindings auto-generated for MatrixService - 1280x800 default window Build: 68MB linux/amd64 binary in build/bin/
This commit is contained in:
+63
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func tempStoreDir() string {
|
||||
return filepath.Join(os.TempDir(), "matrix_client_pc_login")
|
||||
}
|
||||
|
||||
func userStoreDir(userID string) string {
|
||||
cfg, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
cfg = filepath.Join(os.Getenv("HOME"), ".config")
|
||||
}
|
||||
slug := strings.NewReplacer("@", "", ":", "_", "/", "_").Replace(userID)
|
||||
return filepath.Join(cfg, "matrix_client_pc", slug)
|
||||
}
|
||||
|
||||
// whoami issues GET /_matrix/client/v3/account/whoami against the homeserver with the
|
||||
// provided access_token and returns (user_id, device_id, err). It's used before
|
||||
// MatrixClientInit because mautrix.NewClient wants user_id up front.
|
||||
func whoami(ctx context.Context, homeserver, accessToken string) (string, string, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
u, err := url.JoinPath(homeserver, "/_matrix/client/v3/account/whoami")
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("joinpath: %w", err)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||
cl := &http.Client{Timeout: 15 * time.Second}
|
||||
resp, err := cl.Do(req)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode != 200 {
|
||||
return "", "", fmt.Errorf("whoami: %s: %s", resp.Status, body)
|
||||
}
|
||||
var out struct {
|
||||
UserID string `json:"user_id"`
|
||||
DeviceID string `json:"device_id"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &out); err != nil {
|
||||
return "", "", fmt.Errorf("whoami parse: %w", err)
|
||||
}
|
||||
return out.UserID, out.DeviceID, nil
|
||||
}
|
||||
Reference in New Issue
Block a user