package gonexapp import ( "encoding/base64" "fmt" "net/http" "strings" ) // Credentials are what an ExApp needs to call Nextcloud as one user. // BaseURL is the Nextcloud instance URL. A trailing slash is tolerated by // callers in this package and is removed when they build a URL. // AppID, AppVersion, and AAVersion are sent as EX-APP-ID, EX-APP-VERSION, // and AA-VERSION. AppSecret and UserID form the AUTHORIZATION-APP-API token. type Credentials struct { BaseURL string AppID string AppVersion string AAVersion string AppSecret string UserID string } // AuthHeaders returns AppAPI headers for a request to Nextcloud. // The set is AA-VERSION, EX-APP-ID, EX-APP-VERSION, AUTHORIZATION-APP-API, // and OCS-APIRequest. AUTHORIZATION-APP-API is base64 of UserID, a colon, // and AppSecret. The method does not return an error; empty fields are sent as empty. func (c Credentials) AuthHeaders() http.Header { h := make(http.Header) h.Set("AA-VERSION", c.AAVersion) h.Set("EX-APP-ID", c.AppID) h.Set("EX-APP-VERSION", c.AppVersion) token := base64.StdEncoding.EncodeToString([]byte(c.UserID + ":" + c.AppSecret)) h.Set("AUTHORIZATION-APP-API", token) h.Set("OCS-APIRequest", "true") return h } // WithUser returns a copy whose UserID is userID. // The receiver is not modified. func (c Credentials) WithUser(userID string) Credentials { out := c out.UserID = userID return out } // UserFromRequest reads the requesting user from AUTHORIZATION-APP-API // on an inbound ExApp request. // It returns an error when the header is missing, is not base64, or contains // no user id before the colon. func UserFromRequest(r *http.Request) (string, error) { raw := r.Header.Get("AUTHORIZATION-APP-API") if raw == "" { return "", fmt.Errorf("missing AUTHORIZATION-APP-API") } decoded, err := base64.StdEncoding.DecodeString(raw) if err != nil { return "", fmt.Errorf("invalid AUTHORIZATION-APP-API: %w", err) } parts := strings.SplitN(string(decoded), ":", 2) if len(parts) < 1 || strings.TrimSpace(parts[0]) == "" { return "", fmt.Errorf("AUTHORIZATION-APP-API has no user id") } return parts[0], nil }