Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Azure AD v2 device auth and purge go-autorest
- Loading branch information
Showing
5 changed files
with
142 additions
and
112 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package auth | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"net/url" | ||
"time" | ||
|
||
"golang.org/x/oauth2" | ||
) | ||
|
||
const ( | ||
DeviceCodeGrantType = "urn:ietf:params:oauth:grant-type:device_code" | ||
) | ||
|
||
type DeviceAuth struct { | ||
Code *DeviceCode | ||
TenantID string | ||
ClientID string | ||
} | ||
|
||
type DeviceCode struct { | ||
DeviceCode string `json:"device_code"` | ||
UserCode string `json:"user_code"` | ||
VerificationURL string `json:"verification_url"` | ||
ExpiresIn int `json:"expires_in"` | ||
Interval int `json:"interval"` | ||
Message string `json:"message"` | ||
} | ||
|
||
type DeviceToken struct { | ||
TokenType string `json:"token_type"` | ||
Scope string `json:"scope"` | ||
ExpiresIn int `json:"expires_in"` | ||
AccessToken string `json:"access_token"` | ||
IDToken string `json:"id_token"` | ||
RefreshToken string `json:"refresh_token"` | ||
} | ||
|
||
type DeviceError struct { | ||
Error string `json:"error"` | ||
ErrorDescription string `json:"error_description"` | ||
} | ||
|
||
func NewDeviceAuth(tenantID, clientID, scope string) (*DeviceAuth, error) { | ||
devicecodeURL := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/devicecode", tenantID) | ||
res, err := http.PostForm(devicecodeURL, url.Values{"client_id": {clientID}, "scope": {scope}}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer res.Body.Close() | ||
if res.StatusCode != http.StatusOK { | ||
b, _ := ioutil.ReadAll(res.Body) | ||
return nil, fmt.Errorf("%s: %s", res.Status, string(b)) | ||
} | ||
da := &DeviceAuth{ClientID: clientID, TenantID: tenantID} | ||
dec := json.NewDecoder(res.Body) | ||
err = dec.Decode(&da.Code) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return da, nil | ||
} | ||
|
||
func (da *DeviceAuth) Message() string { | ||
return da.Code.Message | ||
} | ||
|
||
func (da *DeviceAuth) Poll() (*DeviceToken, error) { | ||
tokenURL := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", da.TenantID) | ||
values := url.Values{ | ||
"client_id": {da.ClientID}, | ||
"grant_type": {DeviceCodeGrantType}, | ||
"device_code": {da.Code.DeviceCode}, | ||
} | ||
interval := da.Code.Interval | ||
if interval == 0 { | ||
interval = 5 | ||
} | ||
for { | ||
time.Sleep(time.Second * time.Duration(interval)) | ||
res, err := http.PostForm(tokenURL, values) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer res.Body.Close() | ||
if res.StatusCode == http.StatusOK { | ||
dt := &DeviceToken{} | ||
dec := json.NewDecoder(res.Body) | ||
err = dec.Decode(dt) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return dt, nil | ||
} | ||
de := &DeviceError{} | ||
dec := json.NewDecoder(res.Body) | ||
err = dec.Decode(de) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if de.Error != "authorization_pending" { | ||
return nil, fmt.Errorf("%s: %s", de.Error, de.ErrorDescription) | ||
} | ||
} | ||
} | ||
|
||
func (dt *DeviceToken) Token() (*oauth2.Token, error) { | ||
return &oauth2.Token{ | ||
TokenType: dt.TokenType, | ||
AccessToken: dt.AccessToken, | ||
RefreshToken: dt.RefreshToken, | ||
}, nil | ||
} | ||
|
||
func (dt *DeviceToken) Client(ctx context.Context) *http.Client { | ||
return oauth2.NewClient(ctx, dt) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,13 @@ | ||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= | ||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= | ||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= | ||
github.com/Azure/go-autorest/autorest/adal v0.6.0 h1:UCTq22yE3RPgbU/8u4scfnnzuCW6pwQ9n+uBtV78ouo= | ||
github.com/Azure/go-autorest/autorest/adal v0.6.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= | ||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= | ||
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= | ||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= | ||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= | ||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= | ||
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= | ||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= | ||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= | ||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= | ||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= | ||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= | ||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | ||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= | ||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= | ||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= | ||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= | ||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= | ||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |