-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added interface for fetching/management of tokens, and moved token re…
…levant code out of Authenticate. This token implementation implements storage of tokens in the filesystem, so the client doesn't have to worry about it.
- Loading branch information
Showing
5 changed files
with
178 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package streamingtwitter | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/garyburd/go-oauth/oauth" | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
) | ||
|
||
var ( | ||
// File permissions for the token file. | ||
tokenFilePermission = os.FileMode(0600) | ||
) | ||
|
||
type Tokener interface { | ||
// Token returns a valid user access token to provide access to Twitter. | ||
// This method also needs to set the app token so valid requests can be made. | ||
Token(*oauth.Client) (*oauth.Credentials, error) | ||
} | ||
|
||
type ClientTokens struct { | ||
// Location to our token storage file (JSON format) | ||
TokenFile string `json:"-"` | ||
// Token for the actual application | ||
App *oauth.Credentials | ||
// Token for the user of the application | ||
User *oauth.Credentials | ||
} | ||
|
||
type ClientTokensError struct { | ||
Msg string | ||
} | ||
|
||
func (e ClientTokensError) Error() string { | ||
return e.Msg | ||
} | ||
|
||
// You get a token for your App from Twitter. Put this within the App section | ||
// of the JSON token file. The user's token will be requested, then written | ||
// and saved to this file. | ||
func (t *ClientTokens) Token(oc *oauth.Client) (*oauth.Credentials, error) { | ||
if t.TokenFile == "" { | ||
return nil, &ClientTokensError{ | ||
Msg: "no token file supplied", | ||
} | ||
} | ||
|
||
cf, err := ioutil.ReadFile(t.TokenFile) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if err := json.Unmarshal(cf, t); err != nil { | ||
return nil, err | ||
} | ||
|
||
if t.App == nil { | ||
return nil, &ClientTokensError{ | ||
Msg: "missing \"App\" token", | ||
} | ||
} | ||
|
||
if t.App.Token == "" || t.App.Secret == "" { | ||
return nil, &ClientTokensError{ | ||
Msg: "missing app's Token or Secret", | ||
} | ||
} | ||
oc.Credentials = *t.App | ||
|
||
var token *oauth.Credentials | ||
if t.User == nil { | ||
token = &oauth.Credentials{} | ||
} else { | ||
token = t.User | ||
} | ||
|
||
if token.Token == "" || token.Secret == "" { | ||
tempCredentials, err := oc.RequestTemporaryCredentials(http.DefaultClient, "oob", nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
url := oc.AuthorizationURL(tempCredentials, nil) | ||
fmt.Fprintf(os.Stdout, "Before we can continue ...\nGo to:\n\n\t%s\n\nAuthorize the application and enter in the verification code: ", url) | ||
|
||
var authCode string | ||
fmt.Scanln(&authCode) | ||
|
||
token, _, err = oc.RequestToken(http.DefaultClient, tempCredentials, authCode) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Save the user token within our token file | ||
t.User = token | ||
save, err := json.Marshal(t) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if err := ioutil.WriteFile(t.TokenFile, save, tokenFilePermission); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return token, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2014 JustAdam (adambell7@gmail.com). All rights reserved. | ||
// License: MIT | ||
package streamingtwitter | ||
|
||
import ( | ||
"github.com/garyburd/go-oauth/oauth" | ||
"testing" | ||
) | ||
|
||
func TestTokenAbsentTokenFileStringError(t *testing.T) { | ||
tokens := &ClientTokens{} | ||
_, err := tokens.Token(&oauth.Client{}) | ||
if err.Error() != "no token file supplied" { | ||
t.Errorf("Expecting error \"no token file supplied\", got %v", err) | ||
} | ||
} | ||
|
||
func TestTokenMissingAppDataError(t *testing.T) { | ||
tokens := &ClientTokens{ | ||
TokenFile: "test_data/tokens-empty.json", | ||
} | ||
_, err := tokens.Token(&oauth.Client{}) | ||
if err.Error() != "missing \"App\" token" { | ||
t.Errorf("Expecting error \"missing \"App\" token\", got %v", err) | ||
} | ||
} | ||
|
||
func TestTokenMissingAppTokenSecretError(t *testing.T) { | ||
tokens := &ClientTokens{ | ||
TokenFile: "test_data/tokens-empty.json", | ||
App: &oauth.Credentials{}, | ||
User: &oauth.Credentials{}, | ||
} | ||
_, err := tokens.Token(&oauth.Client{}) | ||
if err.Error() != "missing app's Token or Secret" { | ||
t.Errorf("Expecting error \"Missing app's Token or Secret\", got %v", err) | ||
} | ||
} | ||
|
||
func TestTokenAccessTokenIsSetInFile(t *testing.T) { | ||
tokens := &ClientTokens{ | ||
TokenFile: "test_data/tokens-empty.json", | ||
App: &oauth.Credentials{ | ||
Token: "app-token", | ||
Secret: "app-secret", | ||
}, | ||
User: &oauth.Credentials{ | ||
Token: "user-token", | ||
Secret: "user-secret", | ||
}, | ||
} | ||
token, _ := tokens.Token(&oauth.Client{}) | ||
if token.Token != "user-token" || token.Secret != "user-secret" { | ||
t.Errorf("Client access token not set.") | ||
} | ||
} | ||
|
||
func TestTokenErrorOutput(t *testing.T) { | ||
err := &ClientTokensError{ | ||
Msg: "error message", | ||
} | ||
|
||
if err.Error() != "error message" { | ||
t.Errorf("Expecting \"error message\", got %v", err) | ||
} | ||
} |