1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENSE file for details.
14
type UbuntuSSOServer struct {
16
tokenRegistrationUrl string
19
// tokenURL returns the URL where the Ubuntu SSO tokens can be requested.
20
func (server UbuntuSSOServer) tokenURL() string {
21
return server.baseUrl + "/api/v2/tokens/oauth"
24
// AccountURL returns the URL where the Ubuntu SSO account information can be
26
func (server UbuntuSSOServer) AccountsURL() string {
27
return server.baseUrl + "/api/v2/accounts/"
30
// TokenDetailURL returns the URL where the Ubuntu SSO token details can be
32
func (server UbuntuSSOServer) TokenDetailsURL() string {
33
return server.baseUrl + "/api/v2/tokens/oauth/"
36
// LoginURL returns the url for Openid login
37
func (server UbuntuSSOServer) LoginURL() string {
41
// ProductionUbuntuSSOServer represents the production Ubuntu SSO server
42
// located at https://login.ubuntu.com.
43
var ProductionUbuntuSSOServer = UbuntuSSOServer{"https://login.ubuntu.com", "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/"}
45
// StagingUbuntuSSOServer represents the staging Ubuntu SSO server located
46
// at https://login.staging.ubuntu.com. Use it for testing.
47
var StagingUbuntuSSOServer = UbuntuSSOServer{"https://login.staging.ubuntu.com", "https://one.staging.ubuntu.com/oauth/sso-finished-so-get-tokens/"}
49
// Giving user credentials and token name, retrieves oauth credentials
50
// for the users, the oauth credentials can be used later to sign requests.
51
func (server UbuntuSSOServer) GetToken(email string, password string, tokenName string) (*SSOData, error) {
52
return server.GetTokenWithOTP(email, password, "", tokenName)
55
// GetTokenWithOTP retrieves an oauth token from the Ubuntu SSO server.
56
// Using the user credentials including two-factor authentication and the
57
// token name, an oauth token is retrieved that can later be used to sign
58
// requests. If otp is blank then this is identical to GetToken.
59
func (server UbuntuSSOServer) GetTokenWithOTP(email, password, otp, tokenName string) (*SSOData, error) {
60
credentials := map[string]string{
63
"token_name": tokenName,
66
credentials["otp"] = otp
68
jsonCredentials, err := json.Marshal(credentials)
72
response, err := http.Post(
75
strings.NewReader(string(jsonCredentials)))
79
if response.StatusCode == 404 {
80
return nil, fmt.Errorf("Wrong credentials.")
82
if response.StatusCode != 200 && response.StatusCode != 201 {
83
return nil, fmt.Errorf("SSO Error: %s\n", response.Status)
85
body, err := ioutil.ReadAll(response.Body)
90
err = json.Unmarshal(body, &ssodata)
98
// Returns all the Ubuntu SSO information related to this account.
99
func (server UbuntuSSOServer) GetAccounts(ssodata *SSOData) (string, error) {
100
rp := RequestParameters{
101
BaseURL: server.AccountsURL() + ssodata.ConsumerKey,
103
SignatureMethod: HMACSHA1{}}
105
request, err := http.NewRequest(rp.HTTPMethod, rp.BaseURL, nil)
109
err = SignRequest(ssodata, &rp, request)
113
client := &http.Client{}
114
response, err := client.Do(request)
119
body, err := ioutil.ReadAll(response.Body)
123
if response.StatusCode == 200 {
124
return string(body), nil
126
var jsonMap map[string]interface{}
127
err = json.Unmarshal(body, &jsonMap)
128
// In theory, this should never happen.
130
return "", fmt.Errorf("NO_JSON_RESPONSE")
132
code, ok := jsonMap["code"]
134
return "", fmt.Errorf("NO_CODE")
136
return "", fmt.Errorf("%v", code)
140
// Given oauth credentials and a request, return it signed.
142
ssodata *SSOData, rp *RequestParameters, request *http.Request) error {
143
return ssodata.SignRequest(rp, request)
146
// Given oauth credentials return a valid http authorization header.
147
func GetAuthorizationHeader(
148
ssodata *SSOData, rp *RequestParameters) (string, error) {
149
header, err := ssodata.GetAuthorizationHeader(rp)
153
// Returns all the Ubuntu SSO information related to this token.
154
func (server UbuntuSSOServer) GetTokenDetails(ssodata *SSOData) (string, error) {
155
rp := RequestParameters{
156
BaseURL: server.TokenDetailsURL() + ssodata.TokenKey,
158
SignatureMethod: HMACSHA1{}}
160
request, err := http.NewRequest(rp.HTTPMethod, rp.BaseURL, nil)
164
err = SignRequest(ssodata, &rp, request)
168
client := &http.Client{}
169
response, err := client.Do(request)
173
body, err := ioutil.ReadAll(response.Body)
177
if response.StatusCode == 200 {
178
return string(body), nil
180
var jsonMap map[string]interface{}
181
err = json.Unmarshal(body, &jsonMap)
182
// due to bug #1285176, it is possible to get non json code in the response.
184
return "", fmt.Errorf("INVALID_CREDENTIALS")
186
code, ok := jsonMap["code"]
188
return "", fmt.Errorf("NO_CODE")
190
return "", fmt.Errorf("%v", code)
194
// Verify the validity of the token, abusing the API to get the token details.
195
func (server UbuntuSSOServer) IsTokenValid(ssodata *SSOData) (bool, error) {
196
details, err := server.GetTokenDetails(ssodata)
197
if details != "" && err == nil {