-
Notifications
You must be signed in to change notification settings - Fork 49
feat: oauth feature #267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: oauth feature #267
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,8 @@ import ( | |
"strings" | ||
"time" | ||
|
||
"github.com/twilio/twilio-go/oauth" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/twilio/twilio-go/client/form" | ||
) | ||
|
@@ -27,8 +29,34 @@ func init() { | |
|
||
// Credentials store user authentication credentials. | ||
type Credentials struct { | ||
Username string | ||
Password string | ||
Username string | ||
Password string | ||
ClientCredentials *ClientCredentials | ||
} | ||
|
||
type ClientCredentials struct { | ||
GrantType string | ||
ClientId string | ||
ClientSecret string | ||
RequestHandler RequestHandler | ||
} | ||
|
||
func NewClientCredentials(grantType, clientId, clientSecret string, handler RequestHandler) *ClientCredentials { | ||
return &ClientCredentials{GrantType: grantType, ClientId: clientId, ClientSecret: clientSecret, RequestHandler: handler} | ||
} | ||
Comment on lines
+44
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: not much readable, can we try running a linter (preferably go-fumpt) |
||
|
||
func (c *ClientCredentials) GetAccessToken() (string, error) { | ||
tokenManager := &oauth.TokenManager{ | ||
GrantType: c.GrantType, | ||
ClientId: c.ClientId, | ||
ClientSecret: c.ClientSecret, | ||
Code: "", | ||
Audience: "", | ||
RefreshToken: "", | ||
Scope: "", | ||
} | ||
tokenAuth := oauth.NewTokenAuthInitializer("", tokenManager) | ||
return tokenAuth.FetchToken(c.RequestHandler) | ||
} | ||
|
||
func NewCredentials(username string, password string) *Credentials { | ||
|
@@ -41,6 +69,7 @@ type Client struct { | |
HTTPClient *http.Client | ||
accountSid string | ||
UserAgentExtensions []string | ||
ClientCredentials *ClientCredentials | ||
} | ||
|
||
// default http Client should not follow redirects and return the most recent response. | ||
|
@@ -65,6 +94,10 @@ func (c *Client) SetTimeout(timeout time.Duration) { | |
c.HTTPClient.Timeout = timeout | ||
} | ||
|
||
func (c *Client) SetClientCredentials(clientCredentials *ClientCredentials) { | ||
c.ClientCredentials = clientCredentials | ||
} | ||
|
||
func extractContentTypeHeader(headers map[string]interface{}) (cType string) { | ||
headerType, ok := headers["Content-Type"] | ||
if !ok { | ||
|
@@ -186,6 +219,14 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, | |
if len(c.UserAgentExtensions) > 0 { | ||
userAgent += " " + strings.Join(c.UserAgentExtensions, " ") | ||
} | ||
if c.ClientCredentials != nil { | ||
token, err := c.ClientCredentials.GetAccessToken() | ||
if err != nil { | ||
req.Header.Add("Authorization", "Bearer "+token) | ||
} | ||
} else if c.Username != "" { | ||
req.SetBasicAuth(c.basicAuth()) | ||
} | ||
|
||
req.Header.Add("User-Agent", userAgent) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package oauth | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/golang-jwt/jwt" | ||
"github.com/twilio/twilio-go/client" | ||
) | ||
|
||
type TokenAuth struct { | ||
token string | ||
tokenManager *TokenManager | ||
} | ||
|
||
func NewTokenAuthInitializer(token string, tokenManager *TokenManager) *TokenAuth { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can rename |
||
return &TokenAuth{ | ||
token: token, | ||
tokenManager: tokenManager, | ||
} | ||
} | ||
func NewTokenAuth(grantType, clientId, clientSecret, code, audience, refreshToken, scope string) *TokenAuth { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI, this is not being used anywhere. |
||
return &TokenAuth{ | ||
tokenManager: &TokenManager{ | ||
GrantType: grantType, | ||
ClientId: clientId, | ||
ClientSecret: clientSecret, | ||
Code: code, | ||
Audience: audience, | ||
RefreshToken: refreshToken, | ||
Scope: scope, | ||
}, | ||
} | ||
} | ||
|
||
func (t *TokenAuth) FetchToken(c client.RequestHandler) (string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good to have func level comment to get quick glimpse of what func does and also helps when func is referenced in other packages. |
||
if t.token != "" && !t.TokenExpired() { | ||
return t.token, nil | ||
} | ||
|
||
token, err := t.tokenManager.fetchAccessToken(c) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
t.token = token | ||
return t.token, nil | ||
} | ||
|
||
func (t *TokenAuth) TokenExpired() bool { | ||
token, _, err := new(jwt.Parser).ParseUnverified(t.token, jwt.MapClaims{}) | ||
if err != nil { | ||
return true | ||
} | ||
|
||
if claims, ok := token.Claims.(jwt.MapClaims); ok { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need to catch err here if token.Claims do not assert to |
||
if exp, ok := claims["exp"].(float64); ok { | ||
expirationTime := time.Unix(int64(exp), 0) | ||
return time.Now().After(expirationTime) | ||
} | ||
} | ||
return true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package oauth | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/twilio/twilio-go/client" | ||
preview_iam "github.com/twilio/twilio-go/rest/preview_iam/v1" | ||
) | ||
|
||
type TokenManager struct { | ||
GrantType string | ||
ClientId string | ||
ClientSecret string | ||
Code string | ||
Audience string | ||
RefreshToken string | ||
Scope string | ||
} | ||
|
||
func NewTokenManager(grantType, clientId, clientSecret, code, audience, refreshToken, scope string) *TokenManager { | ||
return &TokenManager{ | ||
GrantType: grantType, | ||
ClientId: clientId, | ||
ClientSecret: clientSecret, | ||
Code: code, | ||
Audience: audience, | ||
RefreshToken: refreshToken, | ||
Scope: scope, | ||
} | ||
} | ||
|
||
func (tm *TokenManager) fetchAccessToken(c client.RequestHandler) (string, error) { | ||
params := &preview_iam.CreateTokenParams{} | ||
params.SetGrantType(tm.GrantType). | ||
SetClientId(tm.ClientId). | ||
SetClientSecret(tm.ClientSecret). | ||
SetCode(tm.Code). | ||
SetAudience(tm.Audience). | ||
SetRefreshToken(tm.RefreshToken). | ||
SetScope(tm.Scope) | ||
|
||
token, err := preview_iam.NewApiService(&c).CreateToken(params) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if token.AccessToken == nil { | ||
return "", fmt.Errorf("access token is nil") | ||
} | ||
|
||
return *token.AccessToken, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Go API client for openapi | ||
|
||
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) | ||
|
||
## Overview | ||
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project from the OpenAPI specs located at [twilio/twilio-oai](https://github.com/twilio/twilio-oai/tree/main/spec). By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client. | ||
|
||
- API version: 1.0.0 | ||
- Package version: 1.0.0 | ||
- Build package: com.twilio.oai.TwilioGoGenerator | ||
For more information, please visit [https://support.twilio.com](https://support.twilio.com) | ||
|
||
## Installation | ||
|
||
Install the following dependencies: | ||
|
||
```shell | ||
go get github.com/stretchr/testify/assert | ||
go get golang.org/x/net/context | ||
``` | ||
|
||
Put the package under your project folder and add the following in import: | ||
|
||
```golang | ||
import "./openapi" | ||
``` | ||
|
||
## Documentation for API Endpoints | ||
|
||
All URIs are relative to *https://preview-iam.twilio.com* | ||
|
||
Class | Method | HTTP request | Description | ||
------------ | ------------- | ------------- | ------------- | ||
*AuthorizeApi* | [**FetchAuthorize**](docs/AuthorizeApi.md#fetchauthorize) | **Get** /v1/authorize | Retrieves authorize uri | ||
*TokenApi* | [**CreateToken**](docs/TokenApi.md#createtoken) | **Post** /v1/token | Issues a new Access token (optionally identity_token & refresh_token) in exchange of Oauth grant | ||
|
||
|
||
## Documentation For Models | ||
|
||
- [ScimError](docs/ScimError.md) | ||
- [ScimUser](docs/ScimUser.md) | ||
- [PublicApiCreateRoleAssignmentRequest](docs/PublicApiCreateRoleAssignmentRequest.md) | ||
- [ScimMeta](docs/ScimMeta.md) | ||
- [ScimName](docs/ScimName.md) | ||
- [PublicApiRoleAssignmentResponse](docs/PublicApiRoleAssignmentResponse.md) | ||
- [ScimResourceTypes](docs/ScimResourceTypes.md) | ||
- [OauthV1Authorize](docs/OauthV1Authorize.md) | ||
- [PublicApiCreateAccountResponse](docs/PublicApiCreateAccountResponse.md) | ||
- [OauthV1Token](docs/OauthV1Token.md) | ||
- [PublicApiCreateAccountRequest](docs/PublicApiCreateAccountRequest.md) | ||
- [TwilioServiceErrorResponse](docs/TwilioServiceErrorResponse.md) | ||
- [PublicApiAccountResponsePage](docs/PublicApiAccountResponsePage.md) | ||
- [ScimPatchRequest](docs/ScimPatchRequest.md) | ||
- [PublicApiCreateRoleAssignmentResponsePage](docs/PublicApiCreateRoleAssignmentResponsePage.md) | ||
- [ScimUserPage](docs/ScimUserPage.md) | ||
- [JsonPatch](docs/JsonPatch.md) | ||
- [ScimEmailAddress](docs/ScimEmailAddress.md) | ||
- [PublicApiAccountResponsePageMeta](docs/PublicApiAccountResponsePageMeta.md) | ||
- [PublicApiAccountResponse](docs/PublicApiAccountResponse.md) | ||
- [ScimPatchOperation](docs/ScimPatchOperation.md) | ||
- [ScimResourceTypesResources](docs/ScimResourceTypesResources.md) | ||
|
||
|
||
## Documentation For Authorization | ||
|
||
|
||
|
||
## oAuth2ClientCredentials | ||
|
||
|
||
- **Type**: OAuth | ||
- **Flow**: application | ||
- **Authorization URL**: | ||
- **Scopes**: N/A | ||
|
||
Example | ||
|
||
```golang | ||
auth := context.WithValue(context.Background(), sw.ContextAccessToken, "ACCESSTOKENSTRING") | ||
r, err := client.Service.Operation(auth, args) | ||
``` | ||
|
||
Or via OAuth2 module to automatically refresh tokens and perform user authentication. | ||
|
||
```golang | ||
import "golang.org/x/oauth2" | ||
|
||
/* Perform OAuth2 round trip request and obtain a token */ | ||
|
||
tokenSource := oauth2cfg.TokenSource(createContext(httpClient), &token) | ||
auth := context.WithValue(oauth2.NoContext, sw.ContextOAuth2, tokenSource) | ||
r, err := client.Service.Operation(auth, args) | ||
``` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* This code was generated by | ||
* ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __ | ||
* | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/ | ||
* | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \ | ||
* | ||
* Organization Public API | ||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) | ||
* | ||
* NOTE: This class is auto generated by OpenAPI Generator. | ||
* https://openapi-generator.tech | ||
* Do not edit the class manually. | ||
*/ | ||
|
||
package openapi | ||
|
||
import ( | ||
twilio "github.com/twilio/twilio-go/client" | ||
) | ||
|
||
type ApiService struct { | ||
baseURL string | ||
requestHandler *twilio.RequestHandler | ||
} | ||
|
||
func NewApiService(requestHandler *twilio.RequestHandler) *ApiService { | ||
return &ApiService{ | ||
requestHandler: requestHandler, | ||
baseURL: "https://preview-iam.twilio.com", | ||
} | ||
} | ||
|
||
func NewApiServiceWithClient(client twilio.BaseClient) *ApiService { | ||
return NewApiService(twilio.NewRequestHandler(client)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: import ordering.