124 lines
3.1 KiB
Go
124 lines
3.1 KiB
Go
package bot
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.secnex.io/secnex/masterlog"
|
|
)
|
|
|
|
var AUTH *Auth
|
|
|
|
type Token struct {
|
|
Data map[string]interface{}
|
|
ExpiresIn int `json:"expires_in"`
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
AccessToken string `json:"access_token"`
|
|
}
|
|
|
|
type Auth struct {
|
|
TenantId string
|
|
AppId string
|
|
AppSecret string
|
|
token *Token
|
|
}
|
|
|
|
func NewAuth(tenantId, appId, appSecret string) *Auth {
|
|
auth := &Auth{
|
|
TenantId: tenantId,
|
|
AppId: appId,
|
|
AppSecret: appSecret,
|
|
}
|
|
AUTH = auth
|
|
return auth
|
|
}
|
|
|
|
func (a *Auth) getRequestUrl() string {
|
|
return fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", a.TenantId)
|
|
}
|
|
|
|
func (a *Auth) requestToken() (*Token, error) {
|
|
if a.TenantId == "" {
|
|
return nil, fmt.Errorf("tenant ID is required but not set")
|
|
}
|
|
if a.AppId == "" {
|
|
return nil, fmt.Errorf("app ID is required but not set")
|
|
}
|
|
if a.AppSecret == "" {
|
|
return nil, fmt.Errorf("app secret is required but not set")
|
|
}
|
|
|
|
requestUrl := a.getRequestUrl()
|
|
masterlog.Debug("Requesting token", map[string]interface{}{
|
|
"url": requestUrl,
|
|
"tenantId": a.TenantId,
|
|
"appId": a.AppId,
|
|
})
|
|
|
|
body := url.Values{
|
|
"grant_type": []string{"client_credentials"},
|
|
"client_id": []string{a.AppId},
|
|
"client_secret": []string{a.AppSecret},
|
|
"scope": []string{"https://api.botframework.com/.default"},
|
|
}
|
|
req, err := http.NewRequest("POST", requestUrl, strings.NewReader(body.Encode()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
bodyBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
masterlog.Error("Token request failed", map[string]interface{}{
|
|
"statusCode": resp.StatusCode,
|
|
})
|
|
return nil, fmt.Errorf("token request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
masterlog.Debug("Token request successful", map[string]interface{}{
|
|
"statusCode": resp.StatusCode,
|
|
})
|
|
|
|
if len(bodyBytes) == 0 {
|
|
return nil, fmt.Errorf("empty response body from token endpoint")
|
|
}
|
|
|
|
var token Token
|
|
err = json.Unmarshal(bodyBytes, &token)
|
|
if err != nil {
|
|
masterlog.Error("Failed to unmarshal token response", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return nil, fmt.Errorf("failed to unmarshal token response: %w", err)
|
|
}
|
|
token.ExpiresAt = time.Now().Add(time.Duration(token.ExpiresIn) * time.Second)
|
|
masterlog.Debug("Requested token", map[string]interface{}{"accessToken": token.AccessToken, "expiresIn": token.ExpiresIn, "expiresAt": token.ExpiresAt})
|
|
return &token, nil
|
|
}
|
|
|
|
func (a *Auth) GetToken() (string, error) {
|
|
if a.token == nil || a.token.ExpiresAt.Before(time.Now()) {
|
|
token, err := a.requestToken()
|
|
if err != nil {
|
|
masterlog.Error("Failed to refresh token", map[string]interface{}{"error": err.Error()})
|
|
return "", err
|
|
}
|
|
a.token = token
|
|
masterlog.Debug("Refreshed token", map[string]interface{}{"token": a.token.AccessToken})
|
|
}
|
|
return a.token.AccessToken, nil
|
|
}
|