auth
2014-09-01
auth/authorize.go.old
Deprecate old implementations. Let's remove all of the osin stuff altogether, in favour of a more testable, unit-based approach. Leave all the old files around, for easy reference, but add the .old suffix so the go tools don't pick them up.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/authorize.go.old Mon Sep 01 09:13:52 2014 -0400 1.3 @@ -0,0 +1,272 @@ 1.4 +package auth 1.5 + 1.6 +import ( 1.7 + "net/http" 1.8 + "net/url" 1.9 + "time" 1.10 + 1.11 + "strings" 1.12 + "secondbit.org/uuid" 1.13 +) 1.14 + 1.15 +// AuthorizeRequestType is the type for OAuth param `response_type` 1.16 +type AuthorizeRequestType string 1.17 + 1.18 +const ( 1.19 + CodeAuthRT AuthorizeRequestType = "code" 1.20 + TokenAuthRT = "token" 1.21 +) 1.22 + 1.23 +type AuthRequest struct { 1.24 + Client Client 1.25 + Scope string 1.26 + RedirectURI string 1.27 + State string 1.28 +} 1.29 + 1.30 +// Authorization data 1.31 +type AuthorizeData struct { 1.32 + // Authorization code 1.33 + Code string 1.34 + 1.35 + // Token expiration in seconds 1.36 + ExpiresIn int32 1.37 + 1.38 + // Date created 1.39 + CreatedAt time.Time 1.40 + 1.41 + ProfileID uuid.ID 1.42 + 1.43 + AuthRequest 1.44 +} 1.45 + 1.46 +// IsExpired is true if authorization expired 1.47 +func (d *AuthorizeData) IsExpired() bool { 1.48 + return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now()) 1.49 +} 1.50 + 1.51 +// ExpireAt returns the expiration date 1.52 +func (d *AuthorizeData) ExpireAt() time.Time { 1.53 + return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second) 1.54 +} 1.55 + 1.56 +// HandleAuthorizeRequest is the main http.HandlerFunc for handling 1.57 +// authorization requests 1.58 +func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.59 + r.ParseForm() 1.60 + // create the authorization request 1.61 + redirectURI := r.Form.Get("redirect_uri") 1.62 + var err error 1.63 + if redirectURI != "" { 1.64 + redirectURI, err = url.QueryUnescape(redirectURI) 1.65 + if err != nil { 1.66 + ctx.RenderError(w, URIFormatError(redirectURI)) 1.67 + return 1.68 + } 1.69 + } 1.70 + 1.71 + state := r.Form.Get("state") 1.72 + scope := r.Form.Get("scope") 1.73 + 1.74 + // must have a valid client 1.75 + id, err := uuid.Parse(r.Form.Get("client_id")) 1.76 + if err != nil { 1.77 + ctx.RenderError(w, InvalidClientIDError(r.Form.Get("client_id"))) 1.78 + return 1.79 + } 1.80 + client, err := GetClient(id, ctx) 1.81 + if err != nil { 1.82 + if err == ClientNotFoundError { 1.83 + ctx.RenderError(w, ClientNotFoundError) 1.84 + return 1.85 + } 1.86 + if redirectURI == "" { 1.87 + ctx.RenderError(w, URIMissingError) 1.88 + return 1.89 + } 1.90 + req := AuthRequest{ 1.91 + RedirectURI: redirectURI, 1.92 + Scope: scope, 1.93 + State: state, 1.94 + } 1.95 + redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain) 1.96 + if err != nil { 1.97 + ctx.RenderError(w, URIFormatError(redirectURI)) 1.98 + return 1.99 + } 1.100 + http.Redirect(w, r, redir, http.StatusFound) 1.101 + return 1.102 + } 1.103 + if client.RedirectURI == "" { 1.104 + ctx.RenderError(w, URIMissingError) 1.105 + return 1.106 + } 1.107 + 1.108 + // check redirect uri 1.109 + if redirectURI == "" { 1.110 + redirectURI = client.RedirectURI 1.111 + } 1.112 + if err = validateURI(client.RedirectURI, redirectURI); err != nil { 1.113 + ctx.RenderError(w, NewURIMismatchError(client.RedirectURI, redirectURI)) 1.114 + return 1.115 + } 1.116 + 1.117 + req := AuthRequest{ 1.118 + Client: client, 1.119 + RedirectURI: redirectURI, 1.120 + Scope: scope, 1.121 + State: state, 1.122 + } 1.123 + 1.124 + requestType := AuthorizeRequestType(r.Form.Get("response_type")) 1.125 + if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) { 1.126 + switch requestType { 1.127 + case CodeAuthRT: 1.128 + req.handleCodeRequest(w, r, ctx) 1.129 + return 1.130 + case TokenAuthRT: 1.131 + req.handleTokenRequest(w, r, ctx) 1.132 + return 1.133 + } 1.134 + } 1.135 + redir, err := req.GetErrorRedirect(ErrorInvalidRequest, "Invalid response type.", ctx.Config.DocumentationDomain) 1.136 + if err != nil { 1.137 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 1.138 + return 1.139 + } 1.140 + http.Redirect(w, r, redir, http.StatusFound) 1.141 +} 1.142 + 1.143 +func (req AuthRequest) handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.144 + if r.Method != "GET" && r.Method != "POST" { 1.145 + ctx.RenderError(w, InvalidMethodError) 1.146 + return 1.147 + } 1.148 + 1.149 + if err := validateSession(r, ctx); err == ErrorNotAuthenticated { 1.150 + http.Redirect(w, r, "/auth/login?redirect_to="+url.QueryEscape(r.URL.String()), http.StatusFound) 1.151 + return 1.152 + } else if err != nil { 1.153 + ctx.RenderError(w, err) 1.154 + return 1.155 + } 1.156 + 1.157 + if r.Method == "GET" { 1.158 + ctx.RenderConfirmation(w, r, req) 1.159 + return 1.160 + } 1.161 + 1.162 + if r.FormValue("approved") != "true" { 1.163 + redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain) 1.164 + if err != nil { 1.165 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 1.166 + return 1.167 + } 1.168 + http.Redirect(w, r, redir, http.StatusFound) 1.169 + return 1.170 + } 1.171 + 1.172 + data := AuthorizeData{AuthRequest: req} 1.173 + 1.174 + data.ExpiresIn = ctx.Config.AuthorizationExpiration 1.175 + data.Code = newToken() 1.176 + data.CreatedAt = time.Now() 1.177 + 1.178 + err := ctx.Tokens.SaveAuthorization(data) 1.179 + if err != nil { 1.180 + redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain) 1.181 + if err != nil { 1.182 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 1.183 + return 1.184 + } 1.185 + http.Redirect(w, r, redir, http.StatusFound) 1.186 + return 1.187 + } 1.188 + 1.189 + redir, err := data.GetRedirect() 1.190 + if err != nil { 1.191 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 1.192 + return 1.193 + } 1.194 + http.Redirect(w, r, redir, http.StatusFound) 1.195 +} 1.196 + 1.197 +func (req AuthRequest) handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.198 + if r.Method != "GET" && r.Method != "POST" { 1.199 + ctx.RenderError(w, InvalidMethodError) 1.200 + return 1.201 + } 1.202 + 1.203 + if err := validateSession(r, ctx); err == ErrorNotAuthenticated { 1.204 + http.Redirect(w, r, "/auth/login?redirect_to="+url.QueryEscape(r.URL.String()), http.StatusFound) 1.205 + return 1.206 + } else if err != nil { 1.207 + ctx.RenderError(w, err) 1.208 + return 1.209 + } 1.210 + 1.211 + if r.Method == "GET" { 1.212 + ctx.RenderConfirmation(w, r, req) 1.213 + return 1.214 + } 1.215 + 1.216 + if r.FormValue("approved") != "true" { 1.217 + redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain) 1.218 + if err != nil { 1.219 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 1.220 + return 1.221 + } 1.222 + http.Redirect(w, r, redir, http.StatusFound) 1.223 + return 1.224 + } 1.225 + 1.226 + data := AccessData{AuthRequest: req} 1.227 + 1.228 + err := fillTokens(&data, false, ctx) 1.229 + if err != nil { 1.230 + ctx.RenderError(w, InternalServerError) 1.231 + return 1.232 + } 1.233 + 1.234 + redir, err := data.GetRedirect(true) 1.235 + if err != nil { 1.236 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 1.237 + return 1.238 + } 1.239 + http.Redirect(w, r, redir, http.StatusFound) 1.240 +} 1.241 + 1.242 +func (data AuthorizeData) GetRedirect() (string, error) { 1.243 + u, err := url.Parse(data.RedirectURI) 1.244 + if err != nil { 1.245 + return "", err 1.246 + } 1.247 + 1.248 + // add parameters 1.249 + q := u.Query() 1.250 + q.Set("code", data.Code) 1.251 + q.Set("state", data.State) 1.252 + u.RawQuery = q.Encode() 1.253 + 1.254 + return u.String(), nil 1.255 +} 1.256 + 1.257 +func (req AuthRequest) GetErrorRedirect(code, description, uriBase string) (string, error) { 1.258 + u, err := url.Parse(req.RedirectURI) 1.259 + if err != nil { 1.260 + return "", err 1.261 + } 1.262 + 1.263 + // add parameters 1.264 + q := u.Query() 1.265 + q.Set("error", code) 1.266 + q.Set("error_description", description) 1.267 + q.Set("error_uri", strings.Join([]string{ 1.268 + strings.TrimRight(uriBase, "/"), 1.269 + strings.TrimLeft(code, "/"), 1.270 + }, "/")) 1.271 + q.Set("state", req.State) 1.272 + u.RawQuery = q.Encode() 1.273 + 1.274 + return u.String(), nil 1.275 +}