-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathauthy.go
134 lines (113 loc) · 3.87 KB
/
authy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Package authy implements the base methods for implementing oauth providers
// It is recommended instead to use one of the middlewares already provided by the package
//
// Middlewares:
//
// * Martini: https://github.com/gophergala/authy/martini
//
// For a full guide visit https://github.com/gophergala/authy
package authy
import (
"errors"
"fmt"
"github.com/gophergala/authy/oauth2"
"github.com/gophergala/authy/provider"
"net/http"
)
// Authy represents the current configuration and cached provider data
type Authy struct {
config Config
providers map[string]provider.ProviderConfig
}
// Token is returned on a successful auth
type Token struct {
// The provider on which that token can be used
Provider string
// The actual value of the token
Value string
// The scopes returned by the provider, some providers may allow the user to change the scope of an auth request
// Make sure to check the available scopes before doing queries on their webservices
Scope []string
}
// Parse the configuration and build the list of providers, return an Authy instance
func NewAuthy(config Config) (Authy, error) {
var availableProviders = map[string]provider.ProviderConfig{}
// load all providers
for providerName, providerConfig := range config.Providers {
providerData, err := provider.GetProvider(providerName)
if err != nil {
return Authy{}, err
}
providerConfig.Provider = providerData
availableProviders[providerName] = providerConfig
}
return Authy{
config: config,
providers: availableProviders,
}, nil
}
// Generate a CSRF token and store it in the provided session object, return the authorisation URL
// It should be noted that the session object should prevent the user from seeing the sum generated
func (a Authy) Authorize(providerName string, session Session, r *http.Request) (string, error) {
providerConfig, ok := a.providers[providerName]
if ok != true {
return "", errors.New(fmt.Sprintf("unknown provider %s", providerName))
}
if providerConfig.Provider.OAuth == 2 {
state, err := oauth2.NewState()
if err != nil {
return "", err
}
// save authentication state in session
session.Set("authy."+providerName+".state", state)
providerConfig.State = state
// generate authorisation URL
redirectUrl, err := oauth2.AuthorizeURL(providerConfig, r)
if err != nil {
return "", err
}
return redirectUrl, nil
}
return "", errors.New("Not Implemented")
}
// Check the CSRF token then query the distant provider for an access token using the code that was provided by the
// authorization API
func (a Authy) Access(providerName string, session Session, r *http.Request) (Token, string, error) {
providerConfig, ok := a.providers[providerName]
if ok != true {
return Token{}, "", errors.New(fmt.Sprintf("unknown provider %s", providerName))
}
if providerConfig.Provider.OAuth == 2 {
// check the state parameter against CSRF
state := session.Get("authy." + providerName + ".state")
if state == nil {
return Token{}, "", errors.New("state token is not set in session, possible CSRF")
}
stateParam := r.URL.Query().Get("state")
if stateParam != state.(string) {
return Token{}, "", errors.New("invalid state param provided, possible CSRF")
}
// we don't need it anymore
session.Delete("authy." + providerName + ".state")
code := r.URL.Query().Get("code")
if code == "" {
return Token{}, "", errors.New("code was not found in the query parameters")
}
// retrieve access token from provider
token, err := oauth2.GetAccessToken(providerConfig, r)
if err != nil {
return Token{}, "", err
}
// provide the proper callback URL
redirectUrl := a.config.Callback
if providerConfig.Callback != "" {
redirectUrl = providerConfig.Callback
}
// return the token
return Token{
Value: token.AccessToken,
Scope: token.Scope,
}, redirectUrl, nil
}
return Token{}, "", errors.New("Not Implemented")
}