auth

Paddy 2014-08-16 Parent:9fe684b33b3d Child:e6a44cfda658

20:0ccace901036 Go to Latest

auth/authorize.go

Check session before rendering confirmation page. The confirmation page should not be rendered until the session is set. Check the request method, then check the session, then finally render the confirmation page, should we need to.

History
1 package auth
3 import (
4 "net/http"
5 "net/url"
6 "time"
8 "strings"
9 "secondbit.org/uuid"
10 )
12 // AuthorizeRequestType is the type for OAuth param `response_type`
13 type AuthorizeRequestType string
15 const (
16 CodeAuthRT AuthorizeRequestType = "code"
17 TokenAuthRT = "token"
18 )
20 type AuthRequest struct {
21 Client Client
22 Scope string
23 RedirectURI string
24 State string
25 }
27 // Authorization data
28 type AuthorizeData struct {
29 // Authorization code
30 Code string
32 // Token expiration in seconds
33 ExpiresIn int32
35 // Date created
36 CreatedAt time.Time
38 ProfileID uuid.ID
40 AuthRequest
41 }
43 // IsExpired is true if authorization expired
44 func (d *AuthorizeData) IsExpired() bool {
45 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
46 }
48 // ExpireAt returns the expiration date
49 func (d *AuthorizeData) ExpireAt() time.Time {
50 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
51 }
53 // HandleAuthorizeRequest is the main http.HandlerFunc for handling
54 // authorization requests
55 func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
56 r.ParseForm()
57 // create the authorization request
58 redirectURI := r.Form.Get("redirect_uri")
59 var err error
60 if redirectURI != "" {
61 redirectURI, err = url.QueryUnescape(redirectURI)
62 if err != nil {
63 ctx.RenderError(w, URIFormatError(redirectURI))
64 return
65 }
66 }
68 state := r.Form.Get("state")
69 scope := r.Form.Get("scope")
71 // must have a valid client
72 id, err := uuid.Parse(r.Form.Get("client_id"))
73 if err != nil {
74 ctx.RenderError(w, InvalidClientIDError(r.Form.Get("client_id")))
75 return
76 }
77 client, err := GetClient(id, ctx)
78 if err != nil {
79 if err == ClientNotFoundError {
80 ctx.RenderError(w, ClientNotFoundError)
81 return
82 }
83 if redirectURI == "" {
84 ctx.RenderError(w, URIMissingError)
85 return
86 }
87 req := AuthRequest{
88 RedirectURI: redirectURI,
89 Scope: scope,
90 State: state,
91 }
92 redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
93 if err != nil {
94 ctx.RenderError(w, URIFormatError(redirectURI))
95 return
96 }
97 http.Redirect(w, r, redir, http.StatusFound)
98 return
99 }
100 if client.RedirectURI == "" {
101 ctx.RenderError(w, URIMissingError)
102 return
103 }
105 // check redirect uri
106 if redirectURI == "" {
107 redirectURI = client.RedirectURI
108 }
109 if err = validateURI(client.RedirectURI, redirectURI); err != nil {
110 ctx.RenderError(w, NewURIMismatchError(client.RedirectURI, redirectURI))
111 return
112 }
114 req := AuthRequest{
115 Client: client,
116 RedirectURI: redirectURI,
117 Scope: scope,
118 State: state,
119 }
121 requestType := AuthorizeRequestType(r.Form.Get("response_type"))
122 if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) {
123 switch requestType {
124 case CodeAuthRT:
125 req.handleCodeRequest(w, r, ctx)
126 return
127 case TokenAuthRT:
128 req.handleTokenRequest(w, r, ctx)
129 return
130 }
131 }
132 redir, err := req.GetErrorRedirect(ErrorInvalidRequest, "Invalid response type.", ctx.Config.DocumentationDomain)
133 if err != nil {
134 ctx.RenderError(w, URIFormatError(req.RedirectURI))
135 return
136 }
137 http.Redirect(w, r, redir, http.StatusFound)
138 }
140 func (req AuthRequest) handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
141 if r.Method != "GET" && r.Method != "POST" {
142 ctx.RenderError(w, InvalidMethodError)
143 return
144 }
146 if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
147 // TODO: redirect to login
148 return
149 } else if err != nil {
150 ctx.RenderError(w, err)
151 return
152 }
154 if r.Method == "GET" {
155 ctx.RenderConfirmation(w, r, req)
156 return
157 }
159 if r.FormValue("approved") != "true" {
160 redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
161 if err != nil {
162 ctx.RenderError(w, URIFormatError(req.RedirectURI))
163 return
164 }
165 http.Redirect(w, r, redir, http.StatusFound)
166 return
167 }
169 data := AuthorizeData{AuthRequest: req}
171 data.ExpiresIn = ctx.Config.AuthorizationExpiration
172 data.Code = newToken()
173 data.CreatedAt = time.Now()
175 err := ctx.Tokens.SaveAuthorization(data)
176 if err != nil {
177 redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
178 if err != nil {
179 ctx.RenderError(w, URIFormatError(req.RedirectURI))
180 return
181 }
182 http.Redirect(w, r, redir, http.StatusFound)
183 return
184 }
186 redir, err := data.GetRedirect()
187 if err != nil {
188 ctx.RenderError(w, URIFormatError(req.RedirectURI))
189 return
190 }
191 http.Redirect(w, r, redir, http.StatusFound)
192 }
194 func (req AuthRequest) handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
195 if r.Method != "GET" && r.Method != "POST" {
196 ctx.RenderError(w, InvalidMethodError)
197 return
198 }
200 if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
201 // TODO: redirect to login
202 return
203 } else if err != nil {
204 ctx.RenderError(w, err)
205 return
206 }
208 if r.Method == "GET" {
209 ctx.RenderConfirmation(w, r, req)
210 return
211 }
213 if r.FormValue("approved") != "true" {
214 redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
215 if err != nil {
216 ctx.RenderError(w, URIFormatError(req.RedirectURI))
217 return
218 }
219 http.Redirect(w, r, redir, http.StatusFound)
220 return
221 }
223 data := AccessData{AuthRequest: req}
225 err := fillTokens(&data, false, ctx)
226 if err != nil {
227 ctx.RenderError(w, InternalServerError)
228 return
229 }
231 redir, err := data.GetRedirect(true)
232 if err != nil {
233 ctx.RenderError(w, URIFormatError(req.RedirectURI))
234 return
235 }
236 http.Redirect(w, r, redir, http.StatusFound)
237 }
239 func (data AuthorizeData) GetRedirect() (string, error) {
240 u, err := url.Parse(data.RedirectURI)
241 if err != nil {
242 return "", err
243 }
245 // add parameters
246 q := u.Query()
247 q.Set("code", data.Code)
248 q.Set("state", data.State)
249 u.RawQuery = q.Encode()
251 return u.String(), nil
252 }
254 func (req AuthRequest) GetErrorRedirect(code, description, uriBase string) (string, error) {
255 u, err := url.Parse(req.RedirectURI)
256 if err != nil {
257 return "", err
258 }
260 // add parameters
261 q := u.Query()
262 q.Set("error", code)
263 q.Set("error_description", description)
264 q.Set("error_uri", strings.Join([]string{
265 strings.TrimRight(uriBase, "/"),
266 strings.TrimLeft(code, "/"),
267 }, "/"))
268 q.Set("state", req.State)
269 u.RawQuery = q.Encode()
271 return u.String(), nil
272 }