Skip to content

Commit

Permalink
Implement Azure AD v2 device auth and purge go-autorest
Browse files Browse the repository at this point in the history
  • Loading branch information
yaegashi committed Sep 15, 2019
1 parent a49a3f0 commit 618f38c
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 112 deletions.
71 changes: 0 additions & 71 deletions adal/client.go

This file was deleted.

121 changes: 121 additions & 0 deletions auth/deviceauth.go
@@ -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)
}
37 changes: 20 additions & 17 deletions cmd/msgraph-me/main.go
Expand Up @@ -4,20 +4,18 @@ import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
"os"

"github.com/yaegashi/msgraph.go/adal"

"github.com/yaegashi/msgraph.go/auth"
msgraph "github.com/yaegashi/msgraph.go/v1.0"
)

const (
defaultAuthority = "https://login.microsoftonline.com/"
defaultTenantID = "common"
defaultClientID = "45c7f99c-0a94-42ff-a6d8-a8d657229e8c"
defaultResource = "https://graph.microsoft.com"
defaultStore = "token_cache.json"
defaultTenantID = "common"
defaultClientID = "45c7f99c-0a94-42ff-a6d8-a8d657229e8c"
defaultScope = "openid profile user.readwrite files.readwrite.all"
)

func dump(o interface{}) {
Expand All @@ -27,21 +25,25 @@ func dump(o interface{}) {
}

func main() {
adalClient := &adal.Client{}
flag.StringVar(&adalClient.Authority, "authority", defaultAuthority, "Authority")
flag.StringVar(&adalClient.TenantID, "tenant_id", defaultTenantID, "Tenant ID")
flag.StringVar(&adalClient.ClientID, "client_id", defaultClientID, "Client ID")
flag.StringVar(&adalClient.Resource, "resource", defaultResource, "Resource")
flag.StringVar(&adalClient.Store, "store", defaultStore, "Token cache store path")
var tenantID, clientID string
flag.StringVar(&tenantID, "tenant_id", defaultTenantID, "Tenant ID")
flag.StringVar(&clientID, "client_id", defaultClientID, "Client ID")
flag.Parse()

err := adalClient.LoadToken()
da, err := auth.NewDeviceAuth(tenantID, clientID, defaultScope)
if err != nil {
log.Fatal(err)
}

fmt.Println(da.Message())

dt, err := da.Poll()
if err != nil {
log.Fatal(err)
}

ctx := context.Background()
cli := adalClient.NewHTTPClient(ctx)
cli := dt.Client(ctx)
serv := msgraph.NewService(cli)

{
Expand All @@ -57,8 +59,9 @@ func main() {

{
s := serv.Me().Drive().Root().Children()
log.Printf("GET %s", s.URL())
r, err := s.Get()
q := "?$select=name,file,folder,size"
log.Printf("GET %s%s", s.URL(), q)
r, err := s.GetWithPath(q)
if err == nil {
dump(r)
} else {
Expand Down
5 changes: 1 addition & 4 deletions go.mod
Expand Up @@ -2,7 +2,4 @@ module github.com/yaegashi/msgraph.go

go 1.12

require (
github.com/Azure/go-autorest/autorest/adal v0.6.0
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
)
require golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
20 changes: 0 additions & 20 deletions go.sum
@@ -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=

0 comments on commit 618f38c

Please sign in to comment.