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 }