package auth import ( "encoding/json" "fmt" "net/http" "net/url" "os" "git.directme.in/Joren/CanvasArchiver/internal/config" "git.directme.in/Joren/CanvasArchiver/internal/models" ) type Authenticator struct { HTTPClient *http.Client } func NewAuthenticator(client *http.Client) *Authenticator { return &Authenticator{ HTTPClient: client, } } func (a *Authenticator) GetAccessToken() (string, error) { creds, err := LoadCredentials() if err != nil { fmt.Println("--- Initial Canvas Login Required ---") fmt.Printf("Visit: %s/login/oauth2/auth?client_id=%s&response_type=code&redirect_uri=%s\n", config.BaseURL, config.ClientID, url.QueryEscape(config.RedirectURI)) fmt.Print("Enter Code: ") var code string fmt.Scanln(&code) tr, err := a.doTokenRequest(url.Values{ "grant_type": {"authorization_code"}, "client_id": {config.ClientID}, "client_secret": {config.ClientSecret}, "redirect_uri": {config.RedirectURI}, "code": {code}, }) if err != nil { return "", err } SaveCredentials(&models.Credentials{RefreshToken: tr.RefreshToken}) fmt.Println("[+] Login successful.") return tr.AccessToken, nil } fmt.Println("[*] Reusing saved refresh token...") tr, err := a.doTokenRequest(url.Values{ "grant_type": {"refresh_token"}, "client_id": {config.ClientID}, "client_secret": {config.ClientSecret}, "refresh_token": {creds.RefreshToken}, }) if err != nil { return "", err } fmt.Println("[+] Session refreshed.") return tr.AccessToken, nil } func (a *Authenticator) doTokenRequest(v url.Values) (*models.TokenResponse, error) { resp, err := a.HTTPClient.PostForm(config.BaseURL+"/login/oauth2/token", v) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, fmt.Errorf("token request failed: %d", resp.StatusCode) } var tr models.TokenResponse json.NewDecoder(resp.Body).Decode(&tr) return &tr, nil } func SaveCredentials(creds *models.Credentials) { data, _ := json.MarshalIndent(creds, "", " ") os.WriteFile(config.CredsFile, data, 0o644) } func LoadCredentials() (*models.Credentials, error) { data, err := os.ReadFile(config.CredsFile) if err != nil { return nil, err } var creds models.Credentials json.Unmarshal(data, &creds) return &creds, nil }