cluster

Infrastructure files for Nordgedanken and Midnightthoughts.
git clone git://archive.git.mtrnord.blog/MTRNord/cluster.git
Log | Files | Refs | README

session.go (2432B)


      1 // session.go — Matrix session persistence (login, save, restore).
      2 package main
      3 
      4 import (
      5 	"context"
      6 	"encoding/json"
      7 	"fmt"
      8 	"log/slog"
      9 
     10 	"maunium.net/go/mautrix"
     11 	"maunium.net/go/mautrix/id"
     12 )
     13 
     14 type sessionData struct {
     15 	AccessToken string      `json:"access_token"`
     16 	DeviceID    id.DeviceID `json:"device_id"`
     17 }
     18 
     19 // ensureSession loads a saved session from S3 or logs in with the account
     20 // password and persists the new session for future runs.
     21 //
     22 // Sessions are stored age-encrypted under s3Key+".age". Plain-JSON sessions
     23 // written by older tool versions are transparently read as a fallback so
     24 // existing deployments migrate automatically on the next run.
     25 func ensureSession(ctx context.Context, client *mautrix.Client, acc accountCfg, s3Key string) (*sessionData, error) {
     26 	var sess sessionData
     27 
     28 	// Try age-encrypted first (current format).
     29 	if raw, err := getDecryptedAgeFromS3(ctx, s3Key+".age"); err != nil {
     30 		return nil, fmt.Errorf("load session: %w", err)
     31 	} else if raw != nil {
     32 		if err := json.Unmarshal(raw, &sess); err != nil {
     33 			return nil, fmt.Errorf("parse session: %w", err)
     34 		}
     35 	} else {
     36 		// Fallback: plain JSON written by older versions.
     37 		if err := s3GetJSON(ctx, s3Key, &sess); err != nil {
     38 			return nil, fmt.Errorf("load session (legacy): %w", err)
     39 		}
     40 	}
     41 
     42 	if sess.AccessToken != "" {
     43 		slog.Info("Restored existing session", "device_id", sess.DeviceID)
     44 		client.UserID = acc.UserID
     45 		client.DeviceID = sess.DeviceID
     46 		client.AccessToken = sess.AccessToken
     47 		return &sess, nil
     48 	}
     49 
     50 	slog.Info("First run — logging in with password", "user_id", acc.UserID)
     51 	resp, err := client.Login(ctx, &mautrix.ReqLogin{
     52 		Type: mautrix.AuthTypePassword,
     53 		Identifier: mautrix.UserIdentifier{
     54 			Type: mautrix.IdentifierTypeUser,
     55 			User: string(acc.UserID),
     56 		},
     57 		Password:                 acc.Password,
     58 		InitialDeviceDisplayName: "matrix-backup",
     59 	})
     60 	if err != nil {
     61 		return nil, fmt.Errorf("login: %w", err)
     62 	}
     63 	sess = sessionData{
     64 		AccessToken: resp.AccessToken,
     65 		DeviceID:    resp.DeviceID,
     66 	}
     67 	client.UserID = resp.UserID
     68 	client.DeviceID = resp.DeviceID
     69 	client.AccessToken = resp.AccessToken
     70 	slog.Info("Logged in", "device_id", resp.DeviceID)
     71 
     72 	data, err := json.Marshal(sess)
     73 	if err != nil {
     74 		return nil, err
     75 	}
     76 	// Write age-encrypted; appends ".age" to s3Key.
     77 	if err := s3PutAge(ctx, s3Key, data); err != nil {
     78 		return nil, fmt.Errorf("save session: %w", err)
     79 	}
     80 	return &sess, nil
     81 }