auth
2014-07-18
Child:7b9e0fc20256
auth/authorize.go
Start rewriting the repo. This code originally was a carbon copy of https://github.com/RangelReale/osin, but I am methodically stripping out the extensible nature of it for a simpler interface, while simultaneously bringing the style into line with the Ducky style.
1 package oauth2
3 import (
4 "net/http"
5 "net/url"
6 "time"
8 "secondbit.org/uuid"
9 )
11 // AuthorizeRequestType is the type for OAuth param `response_type`
12 type AuthorizeRequestType string
14 const (
15 CodeAuthRT AuthorizeRequestType = "code"
16 TokenAuthRT = "token"
17 )
19 // Authorize request information
20 type AuthorizeRequest struct {
21 Type AuthorizeRequestType
22 Client Client
23 Scope string
24 RedirectURI string
25 State string
27 // Token expiration in seconds. Change if different from default.
28 // If type = TokenAuthRT, this expiration will be for the ACCESS token.
29 Expiration int32
30 }
32 // Authorization data
33 type AuthorizeData struct {
34 // Client information
35 Client Client
37 // Authorization code
38 Code string
40 // Token expiration in seconds
41 ExpiresIn int32
43 // Requested scope
44 Scope string
46 // Redirect URI from request
47 RedirectURI string
49 // State data from request
50 State string
52 // Date created
53 CreatedAt time.Time
54 }
56 // IsExpired is true if authorization expired
57 func (d *AuthorizeData) IsExpired() bool {
58 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
59 }
61 // ExpireAt returns the expiration date
62 func (d *AuthorizeData) ExpireAt() time.Time {
63 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
64 }
66 // HandleAuthorizeRequest is the main http.HandlerFunc for handling
67 // authorization requests
68 func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
69 r.ParseForm()
71 requestType := AuthorizeRequestType(r.Form.Get("response_type"))
72 if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) {
73 switch requestType {
74 case CodeAuthRT:
75 handleCodeRequest(w, r, ctx)
76 return
77 case TokenAuthRT:
78 handleTokenRequest(w, r, ctx)
79 return
80 }
81 }
82 // TODO: return error
83 }
85 func handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
86 // create the authorization request
87 unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri"))
88 if err != nil {
89 unescapedURI = ""
90 }
91 ret := &AuthorizeRequest{
92 Type: CodeAuthRT,
93 State: r.Form.Get("state"),
94 Scope: r.Form.Get("scope"),
95 RedirectURI: unescapedURI,
96 Expiration: ctx.Config.AuthorizationExpiration,
97 }
99 // must have a valid client
100 id, err := uuid.Parse(r.Form.Get("client_id"))
101 if err != nil {
102 // TODO: return error
103 return
104 }
105 ret.Client, err = GetClient(id, ctx)
106 if err != nil {
107 // TODO: return error
108 return
109 }
110 if ret.Client.RedirectURI == "" {
111 // TODO: return error
112 return
113 }
115 // check redirect uri
116 if ret.RedirectURI == "" {
117 ret.RedirectURI = ret.Client.RedirectURI
118 }
119 if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil {
120 // TODO: return error
121 return
122 }
124 // TODO: do redirect with ret data
125 }
127 func handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
128 // create the authorization request
129 unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri"))
130 if err != nil {
131 unescapedURI = ""
132 }
133 ret := &AuthorizeRequest{
134 Type: TokenAuthRT,
135 State: r.Form.Get("state"),
136 Scope: r.Form.Get("scope"),
137 RedirectURI: unescapedURI,
138 // this type will generate a token directly, use access token expiration instead.
139 Expiration: ctx.Config.AccessExpiration,
140 }
142 // must have a valid client
143 id, err := uuid.Parse(r.Form.Get("client_id"))
144 if err != nil {
145 // TODO: return error
146 return
147 }
148 ret.Client, err = GetClient(id, ctx)
149 if err != nil {
150 // TODO: return error
151 return
152 }
153 if ret.Client.RedirectURI == "" {
154 // TODO: return error
155 return
156 }
158 // check redirect uri
159 if ret.RedirectURI == "" {
160 ret.RedirectURI = ret.Client.RedirectURI
161 }
162 if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil {
163 // TODO: return error
164 }
166 // TODO: redirect with ret information
167 }
169 func FinishAuthorizeRequest(w http.ResponseWriter, r *http.Request, ar *AuthorizeRequest, ctx Context) {
170 // TODO: check if authorized?
171 if ar.Type == TokenAuthRT {
172 // TODO: w.SetRedirectFragment(true) was called...
174 // generate token directly
175 ret := AccessRequest{
176 Code: "",
177 Client: ar.Client,
178 RedirectURI: ar.RedirectURI,
179 Scope: ar.Scope,
180 GenerateRefresh: false, // per the RFC, should NOT generate a refresh token in this case
181 Expiration: ar.Expiration,
182 }
183 // TODO: ret.type was implicit
184 // TODO: ret.Authorized was true
185 FinishAccessRequest(w, r, ret, ctx)
186 } else {
187 // generate authorization token
188 ret := AuthorizeData{
189 Client: ar.Client,
190 CreatedAt: time.Now(),
191 ExpiresIn: ar.Expiration,
192 RedirectURI: ar.RedirectURI,
193 State: ar.State,
194 Scope: ar.Scope,
195 Code: newToken(),
196 }
198 // save authorization token
199 err := saveAuthorize(ret, ctx)
200 if err != nil {
201 // TODO: return error
202 return
203 }
205 // TODO: redirect with ret.Code and ret.State
206 }
207 }
209 func loadAuthorize(code string, ctx Context) (AuthorizeData, error) {
210 return AuthorizeData{}, nil
211 }
213 func saveAuthorize(ret AuthorizeData, ctx Context) error {
214 return nil
215 }
217 func removeAuthorize(code string, ctx Context) error {
218 return nil
219 }