github.com/infraboard/keyauth@v0.8.1/apps/provider/auth/dingtalk/auth.go (about)

     1  package dingtalk
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/hmac"
     6  	"crypto/sha256"
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"net/url"
    13  	"strconv"
    14  	"time"
    15  
    16  	"github.com/infraboard/keyauth/apps/provider/auth"
    17  	"github.com/infraboard/mcube/logger/zap"
    18  )
    19  
    20  var (
    21  	client = &http.Client{}
    22  )
    23  
    24  type Response struct {
    25  	ErrCode  int64    `json:"errcode"`
    26  	ErrMsg   string   `json:"errmsg"`
    27  	UserInfo UserInfo `json:"user_info"`
    28  }
    29  
    30  type UserInfo struct {
    31  	Nick    string `json:"nick"`
    32  	OpenID  string `json:"openid"`
    33  	UnionID string `json:"unionid"`
    34  }
    35  
    36  type Dingtalk struct {
    37  	AppID     string `json:"app_id" bson:"app_id"`
    38  	AppSecret string `json:"app_secret" bson:"app_secret"`
    39  }
    40  
    41  func (a *Dingtalk) getSignature(msg []byte) string {
    42  	hmac := hmac.New(sha256.New, []byte(a.AppSecret))
    43  	_, err := hmac.Write(msg)
    44  	if err != nil {
    45  		zap.L().Errorf("GetSignature hmac.Write error", err)
    46  	}
    47  	digest := hmac.Sum(nil)
    48  	return url.QueryEscape(base64.StdEncoding.EncodeToString(digest))
    49  }
    50  
    51  func (a *Dingtalk) accessTokenURL() string {
    52  	timestamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
    53  	signature := a.getSignature([]byte(timestamp))
    54  	accessTokenURL := fmt.Sprintf("https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=%s&timestamp=%s&signature=%s",
    55  		a.AppID,
    56  		timestamp,
    57  		signature)
    58  	return accessTokenURL
    59  }
    60  
    61  // https://ding-doc.dingtalk.com/doc#/serverapi3/mrugr3
    62  // Step 1: To https://oapi.dingtalk.com/connect/qrconnect?appid=APPID&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=REDIRECT_URI
    63  // Step 2.2: Within Callback, get user_info.nick
    64  // POST HTTPS with body { "tmp_auth_code": "23152698ea18304da4d0ce1xxxxx" }  == code ?
    65  // https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=xxx&timestamp=xxx&signature=xxx
    66  // accessKey=appid
    67  // https://ding-doc.dingtalk.com/doc#/serverapi2/kymkv6
    68  func (a *Dingtalk) CodeAuth(req *auth.AuthCodeRequest) error {
    69  	body := fmt.Sprintf(`{"tmp_auth_code": "%s"}`, req.Code)
    70  	request, _ := http.NewRequest("POST", a.accessTokenURL(), bytes.NewReader([]byte(body)))
    71  	request.Header.Set("Content-Type", "application/json")
    72  	// 发起请求
    73  	resp, err := client.Do(request)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	defer resp.Body.Close()
    78  
    79  	// 处理响应
    80  	b, err := ioutil.ReadAll(resp.Body)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	zap.L().Debugf("dingding oauthcode request, req: %s [%s], response: %s", request, string(b))
    85  
    86  	if (resp.StatusCode / 100) != 2 {
    87  		return fmt.Errorf("status code: %d, %s", resp.StatusCode, string(b))
    88  	}
    89  
    90  	ins := Response{}
    91  	err = json.Unmarshal(b, &ins)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	return nil
    97  }