1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
package origin
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"time"
"github.com/r2northstar/atlas/pkg/juno"
)
type NucleusToken string
// GetNucleusToken generates a Nucleus AuthToken from the active session. Note
// that this token generally lasts ~4h.
//
// If errors.Is(err, ErrAuthRequired), you need a new SID.
func GetNucleusToken(ctx context.Context, t http.RoundTripper, sid juno.SID) (NucleusToken, time.Time, error) {
if t == nil {
t = http.DefaultClient.Transport
}
jar, _ := cookiejar.New(nil)
c := &http.Client{
Transport: t,
Jar: jar,
}
sid.AddTo(c.Jar)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://accounts.ea.com/connect/auth?client_id=ORIGIN_JS_SDK&response_type=token&redirect_uri=nucleus:rest&prompt=none&release_type=prod", nil)
if err != nil {
return "", time.Time{}, err
}
req.Header.Set("Referrer", "https://www.origin.com/")
req.Header.Set("Origin", "https://www.origin.com/")
req.Header.Set("Accept-Language", "en-US;q=0.7,en;q=0.3")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36")
resp, err := c.Do(req)
if err != nil {
return "", time.Time{}, fmt.Errorf("get nucleus token: %w", err)
}
defer resp.Body.Close()
buf, err := io.ReadAll(resp.Body)
if err != nil {
return "", time.Time{}, fmt.Errorf("get nucleus token: %w", err)
}
if resp.StatusCode != http.StatusOK {
var obj struct {
ErrorCode string `json:"error_code"`
Error string `json:"error"`
ErrorNumber json.Number `json:"error_number"`
}
if obj.ErrorCode == "login_required" {
return "", time.Time{}, fmt.Errorf("get nucleus token: %w: login required", ErrAuthRequired)
}
if err := json.Unmarshal(buf, &obj); err == nil && obj.Error != "" {
return "", time.Time{}, fmt.Errorf("get nucleus token: %w: error %s: %s (%q)", ErrOrigin, obj.ErrorNumber, obj.ErrorCode, obj.Error)
}
return "", time.Time{}, fmt.Errorf("get nucleus token: request %q: response status %d (%s)", resp.Request.URL.String(), resp.StatusCode, resp.Status)
}
var obj struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn json.Number `json:"expires_in"`
}
if err := json.Unmarshal(buf, &obj); err != nil {
return "", time.Time{}, fmt.Errorf("get nucleus token: %w", err)
}
if obj.AccessToken == "" || obj.ExpiresIn == "" {
return "", time.Time{}, fmt.Errorf("get nucleus token: invalid response %q", string(buf))
}
var expiry time.Time
if v, err := obj.ExpiresIn.Int64(); err == nil {
expiry = time.Now().Add(time.Duration(v) * time.Second)
} else {
return "", time.Time{}, fmt.Errorf("get nucleus token: invalid response %q: invalid expiry: %w", string(buf), err)
}
return NucleusToken(obj.AccessToken), expiry, nil
}
|