auth

Paddy 2014-09-01 Parent:5bf0a5fd1d01 Child:88523dab00a5

30:e0b3064daf02 rewrite_begins Browse Files

Expunge the old stuff. Let's remove every trace of osin from this repo, and update the LICENSE accordingly, as we're now the full copyright holders.

LICENSE README.md access.go.old authorize.go.old config.go.old context.go.old errors.go.old session.go.old util.go.old

     1.1 --- a/LICENSE	Mon Sep 01 09:49:23 2014 -0400
     1.2 +++ b/LICENSE	Mon Sep 01 09:52:28 2014 -0400
     1.3 @@ -1,4 +1,3 @@
     1.4 -Copyright (c) 2013, Rangel Reale
     1.5  Copyright (c) 2014, Second Bit, LLC
     1.6  
     1.7  All rights reserved.
     1.8 @@ -7,5 +6,5 @@
     1.9  
    1.10  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    1.11  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    1.12 -Neither the name of the SIB IT nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    1.13 +Neither the name of the Second Bit, LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    1.14  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     2.1 --- a/README.md	Mon Sep 01 09:49:23 2014 -0400
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,66 +0,0 @@
     2.4 -OSIN
     2.5 -====
     2.6 -
     2.7 -Golang OAuth2 server library
     2.8 -----------------------------
     2.9 -
    2.10 -OSIN is an OAuth2 server library for the Go language, as specified at
    2.11 -http://tools.ietf.org/html/rfc6749 and http://tools.ietf.org/html/draft-ietf-oauth-v2-10.
    2.12 -
    2.13 -Using it, you can build your own OAuth2 authentication service.
    2.14 -
    2.15 -The library implements the majority of the specification, like authorization and token endpoints, and authorization code, implicit, resource owner and client credentials grant types.
    2.16 -
    2.17 -### Dependencies
    2.18 -
    2.19 -* go-uuid (http://code.google.com/p/go-uuid)
    2.20 -
    2.21 -### Example Server
    2.22 -
    2.23 -````go
    2.24 -import "github.com/RangelReale/osin"
    2.25 -
    2.26 -// TestStorage implements the "osin.Storage" interface
    2.27 -server := osin.NewServer(osin.NewServerConfig(), &TestStorage{})
    2.28 -
    2.29 -// Authorization code endpoint
    2.30 -http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
    2.31 -	resp := server.NewResponse()
    2.32 -	if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {
    2.33 -
    2.34 -		// HANDLE LOGIN PAGE HERE
    2.35 -
    2.36 -		ar.Authorized = true
    2.37 -		server.FinishAuthorizeRequest(resp, r, ar)
    2.38 -	}
    2.39 -	osin.OutputJSON(resp, w, r)
    2.40 -})
    2.41 -
    2.42 -// Access token endpoint
    2.43 -http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
    2.44 -	resp := server.NewResponse()
    2.45 -	if ar := server.HandleAccessRequest(resp, r); ar != nil {
    2.46 -		ar.Authorized = true
    2.47 -		server.FinishAccessRequest(resp, r, ar)
    2.48 -	}
    2.49 -	osin.OutputJSON(resp, w, r)
    2.50 -})
    2.51 -
    2.52 -http.ListenAndServe(":14000", nil)
    2.53 -````
    2.54 -
    2.55 -### Example Access
    2.56 -
    2.57 -Open in your web browser:
    2.58 -
    2.59 -````
    2.60 -http://localhost:14000/authorize?response_type=code&client_id=1234&redirect_url=http%3A%2F%2Flocalhost%3A14000%2Fappauth%2Fcode
    2.61 -````
    2.62 -
    2.63 -### License
    2.64 -
    2.65 -The code is licensed using "New BSD" license.
    2.66 -
    2.67 -### Author
    2.68 -
    2.69 -Rangel Reale
     3.1 --- a/access.go.old	Mon Sep 01 09:49:23 2014 -0400
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,415 +0,0 @@
     3.4 -package auth
     3.5 -
     3.6 -import (
     3.7 -	"net/http"
     3.8 -	"net/url"
     3.9 -	"time"
    3.10 -
    3.11 -	"strconv"
    3.12 -	"secondbit.org/uuid"
    3.13 -)
    3.14 -
    3.15 -// GrantType is the type for OAuth param `grant_type`
    3.16 -type GrantType string
    3.17 -
    3.18 -const (
    3.19 -	AuthorizationCodeGrant GrantType = "authorization_code"
    3.20 -	RefreshTokenGrant                = "refresh_token"
    3.21 -	PasswordGrant                    = "password"
    3.22 -	ClientCredentialsGrant           = "client_credentials"
    3.23 -)
    3.24 -
    3.25 -// AccessData represents an access grant (tokens, expiration, client, etc)
    3.26 -type AccessData struct {
    3.27 -	PreviousAuthorizeData *AuthorizeData `json:"-"`
    3.28 -	PreviousAccessData    *AccessData    `json:"-"` // previous access data, when refreshing
    3.29 -	AccessToken           string         `json:"access_token"`
    3.30 -	RefreshToken          string         `json:"refresh_token,omitempty"`
    3.31 -	ExpiresIn             int32          `json:"expires_in"`
    3.32 -	CreatedAt             time.Time      `json:"-"`
    3.33 -	TokenType             string         `json:"token_type"`
    3.34 -	Scope                 string         `json:"scope,omitempty"`
    3.35 -	ProfileID             uuid.ID        `json:"-"`
    3.36 -	AuthRequest           `json:"-"`
    3.37 -}
    3.38 -
    3.39 -// IsExpired returns true if access expired
    3.40 -func (d *AccessData) IsExpired() bool {
    3.41 -	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
    3.42 -}
    3.43 -
    3.44 -// ExpireAt returns the expiration date
    3.45 -func (d *AccessData) ExpireAt() time.Time {
    3.46 -	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
    3.47 -}
    3.48 -
    3.49 -// HandleOAuth2AccessRequest is the http.HandlerFunc for handling access token requests.
    3.50 -func HandleOAuth2AccessRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
    3.51 -	// Only allow GET or POST
    3.52 -	if r.Method != "POST" {
    3.53 -		if r.Method != "GET" || !ctx.Config.AllowGetAccessRequest {
    3.54 -			ctx.RenderJSONError(w, ErrorInvalidRequest, "Invalid request method.", ctx.Config.DocumentationDomain)
    3.55 -			return
    3.56 -		}
    3.57 -	}
    3.58 -
    3.59 -	grantType := GrantType(r.Form.Get("grant_type"))
    3.60 -	if ctx.Config.AllowedAccessTypes.Exists(grantType) {
    3.61 -		switch grantType {
    3.62 -		case AuthorizationCodeGrant:
    3.63 -			handleAuthorizationCodeRequest(w, r, ctx)
    3.64 -		case RefreshTokenGrant:
    3.65 -			handleRefreshTokenRequest(w, r, ctx)
    3.66 -		case PasswordGrant:
    3.67 -			handlePasswordRequest(w, r, ctx)
    3.68 -		case ClientCredentialsGrant:
    3.69 -			handleClientCredentialsRequest(w, r, ctx)
    3.70 -		default:
    3.71 -			ctx.RenderJSONError(w, ErrorUnsupportedGrantType, "Unsupported grant type.", ctx.Config.DocumentationDomain)
    3.72 -			return
    3.73 -		}
    3.74 -	}
    3.75 -}
    3.76 -
    3.77 -func handleAuthorizationCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
    3.78 -	// get client authentication
    3.79 -	auth, err := getClientAuth(r, ctx.Config.AllowClientSecretInParams)
    3.80 -	if err != nil {
    3.81 -		ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
    3.82 -		return
    3.83 -	}
    3.84 -
    3.85 -	code := r.Form.Get("code")
    3.86 -	// "code" is required
    3.87 -	if code == "" {
    3.88 -		ctx.RenderJSONError(w, ErrorInvalidRequest, "Code must be supplied.", ctx.Config.DocumentationDomain)
    3.89 -		return
    3.90 -	}
    3.91 -
    3.92 -	// must have a valid client
    3.93 -	client, err := getClient(auth, ctx)
    3.94 -	if err != nil {
    3.95 -		if err == ClientNotFoundError || err == InvalidClientError {
    3.96 -			ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
    3.97 -			return
    3.98 -		}
    3.99 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.100 -		return
   3.101 -	}
   3.102 -
   3.103 -	// must be a valid authorization code
   3.104 -	authData, err := ctx.Tokens.GetAuthorization(code)
   3.105 -	if err != nil {
   3.106 -		if err == AuthorizationNotFoundError {
   3.107 -			ctx.RenderJSONError(w, ErrorInvalidGrant, "Invalid authorization.", ctx.Config.DocumentationDomain)
   3.108 -			return
   3.109 -		}
   3.110 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.111 -		return
   3.112 -	}
   3.113 -	if authData.RedirectURI == "" {
   3.114 -		ctx.RenderJSONError(w, ErrorInvalidGrant, "Invalid redirect on grant.", ctx.Config.DocumentationDomain)
   3.115 -		return
   3.116 -	}
   3.117 -	if authData.IsExpired() {
   3.118 -		ctx.RenderJSONError(w, ErrorInvalidGrant, "Authorization is expired.", ctx.Config.DocumentationDomain)
   3.119 -		return
   3.120 -	}
   3.121 -
   3.122 -	// code must be from the client
   3.123 -	if !authData.Client.ID.Equal(client.ID) {
   3.124 -		ctx.RenderJSONError(w, ErrorInvalidGrant, "Grant issued to another client.", ctx.Config.DocumentationDomain)
   3.125 -		return
   3.126 -	}
   3.127 -
   3.128 -	// check redirect uri
   3.129 -	redirectURI := r.Form.Get("redirect_uri")
   3.130 -	if redirectURI == "" {
   3.131 -		redirectURI = client.RedirectURI
   3.132 -	}
   3.133 -	if err = validateURI(client.RedirectURI, redirectURI); err != nil {
   3.134 -		ctx.RenderJSONError(w, ErrorInvalidGrant, "Redirect URI doesn't match client.", ctx.Config.DocumentationDomain)
   3.135 -		return
   3.136 -	}
   3.137 -	if authData.RedirectURI != redirectURI {
   3.138 -		ctx.RenderJSONError(w, ErrorInvalidGrant, "Redirect URI doesn't match auth redirect.", ctx.Config.DocumentationDomain)
   3.139 -		return
   3.140 -	}
   3.141 -
   3.142 -	data := AccessData{
   3.143 -		AuthRequest: AuthRequest{
   3.144 -			Client:      client,
   3.145 -			RedirectURI: redirectURI,
   3.146 -			Scope:       authData.Scope,
   3.147 -		},
   3.148 -		Scope: authData.Scope,
   3.149 -		PreviousAuthorizeData: &authData,
   3.150 -	}
   3.151 -
   3.152 -	err = fillTokens(&data, true, ctx)
   3.153 -	if err != nil {
   3.154 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.155 -		return
   3.156 -	}
   3.157 -	ctx.RenderJSONToken(w, data)
   3.158 -}
   3.159 -
   3.160 -func handleRefreshTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
   3.161 -	// get client authentication
   3.162 -	auth, err := getClientAuth(r, ctx.Config.AllowClientSecretInParams)
   3.163 -
   3.164 -	if err != nil {
   3.165 -		ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
   3.166 -		return
   3.167 -	}
   3.168 -
   3.169 -	code := r.Form.Get("refresh_token")
   3.170 -
   3.171 -	// "refresh_token" is required
   3.172 -	if code == "" {
   3.173 -		ctx.RenderJSONError(w, ErrorInvalidRequest, "Missing refresh token.", ctx.Config.DocumentationDomain)
   3.174 -		return
   3.175 -	}
   3.176 -
   3.177 -	// must have a valid client
   3.178 -	client, err := getClient(auth, ctx)
   3.179 -	if err != nil {
   3.180 -		if err == ClientNotFoundError || err == InvalidClientError {
   3.181 -			ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
   3.182 -			return
   3.183 -		}
   3.184 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.185 -		return
   3.186 -	}
   3.187 -
   3.188 -	// must be a valid refresh code
   3.189 -	refreshData, err := ctx.Tokens.GetRefresh(code)
   3.190 -	if err != nil {
   3.191 -		if err == TokenNotFoundError {
   3.192 -			ctx.RenderJSONError(w, ErrorInvalidGrant, "Refresh token not valid.", ctx.Config.DocumentationDomain)
   3.193 -			return
   3.194 -		}
   3.195 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.196 -		return
   3.197 -	}
   3.198 -
   3.199 -	// client must be the same as the previous token
   3.200 -	if !refreshData.Client.ID.Equal(client.ID) {
   3.201 -		ctx.RenderJSONError(w, ErrorInvalidGrant, "Refresh token issued to another client.", ctx.Config.DocumentationDomain)
   3.202 -		return
   3.203 -	}
   3.204 -
   3.205 -	scope := r.Form.Get("scope")
   3.206 -	if scope == "" {
   3.207 -		scope = refreshData.Scope
   3.208 -	}
   3.209 -
   3.210 -	data := AccessData{
   3.211 -		AuthRequest: AuthRequest{
   3.212 -			Client: client,
   3.213 -			Scope:  scope,
   3.214 -		},
   3.215 -		Scope:              scope,
   3.216 -		PreviousAccessData: &refreshData,
   3.217 -	}
   3.218 -	err = fillTokens(&data, true, ctx)
   3.219 -	if err != nil {
   3.220 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.221 -		return
   3.222 -	}
   3.223 -	ctx.RenderJSONToken(w, data)
   3.224 -}
   3.225 -
   3.226 -func handlePasswordRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
   3.227 -	// get client authentication
   3.228 -	auth, err := getClientAuth(r, ctx.Config.AllowClientSecretInParams)
   3.229 -	if err != nil {
   3.230 -		ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
   3.231 -		return
   3.232 -	}
   3.233 -
   3.234 -	username := r.Form.Get("username")
   3.235 -	password := r.Form.Get("password")
   3.236 -	scope := r.Form.Get("scope")
   3.237 -
   3.238 -	// "username" and "password" is required
   3.239 -	if username == "" || password == "" {
   3.240 -		ctx.RenderJSONError(w, ErrorInvalidRequest, "Missing credentials.", ctx.Config.DocumentationDomain)
   3.241 -		return
   3.242 -	}
   3.243 -
   3.244 -	// must have a valid client
   3.245 -	client, err := getClient(auth, ctx)
   3.246 -	if err != nil {
   3.247 -		if err == ClientNotFoundError || err == InvalidClientError {
   3.248 -			ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
   3.249 -			return
   3.250 -		}
   3.251 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.252 -		return
   3.253 -	}
   3.254 -
   3.255 -	_, err = ctx.Profiles.GetProfile(username, password)
   3.256 -	if err != nil {
   3.257 -		if err == ErrProfileNotFound {
   3.258 -			ctx.RenderJSONError(w, ErrorInvalidGrant, "Invalid credentials.", ctx.Config.DocumentationDomain)
   3.259 -			return
   3.260 -		}
   3.261 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.262 -		return
   3.263 -	}
   3.264 -
   3.265 -	data := AccessData{
   3.266 -		AuthRequest: AuthRequest{
   3.267 -			Client: client,
   3.268 -			Scope:  scope,
   3.269 -		},
   3.270 -		Scope: scope,
   3.271 -	}
   3.272 -
   3.273 -	err = fillTokens(&data, true, ctx)
   3.274 -	if err != nil {
   3.275 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.276 -		return
   3.277 -	}
   3.278 -	ctx.RenderJSONToken(w, data)
   3.279 -}
   3.280 -
   3.281 -func handleClientCredentialsRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
   3.282 -	// get client authentication
   3.283 -	auth, err := getClientAuth(r, ctx.Config.AllowClientSecretInParams)
   3.284 -	if err != nil {
   3.285 -		ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
   3.286 -		return
   3.287 -	}
   3.288 -
   3.289 -	scope := r.Form.Get("scope")
   3.290 -
   3.291 -	// must have a valid client
   3.292 -	client, err := getClient(auth, ctx)
   3.293 -	if err != nil {
   3.294 -		if err == ClientNotFoundError || err == InvalidClientError {
   3.295 -			ctx.RenderJSONError(w, ErrorInvalidClient, "Invalid client auth.", ctx.Config.DocumentationDomain)
   3.296 -			return
   3.297 -		}
   3.298 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.299 -		return
   3.300 -	}
   3.301 -
   3.302 -	data := AccessData{
   3.303 -		AuthRequest: AuthRequest{
   3.304 -			Client: client,
   3.305 -			Scope:  scope,
   3.306 -		},
   3.307 -		Scope: scope,
   3.308 -	}
   3.309 -
   3.310 -	err = fillTokens(&data, true, ctx)
   3.311 -	if err != nil {
   3.312 -		ctx.RenderJSONError(w, ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   3.313 -		return
   3.314 -	}
   3.315 -	ctx.RenderJSONToken(w, data)
   3.316 -}
   3.317 -
   3.318 -func fillTokens(data *AccessData, includeRefresh bool, ctx Context) error {
   3.319 -	var err error
   3.320 -
   3.321 -	// generate access token
   3.322 -	data.AccessToken = newToken()
   3.323 -	if includeRefresh {
   3.324 -		data.RefreshToken = newToken()
   3.325 -	}
   3.326 -
   3.327 -	// save access token
   3.328 -	err = ctx.Tokens.SaveAccess(*data)
   3.329 -	if err != nil {
   3.330 -		if ctx.Log != nil {
   3.331 -			ctx.Log.Printf("Error writing access token: %s\n", err)
   3.332 -		}
   3.333 -		return InternalServerError
   3.334 -	}
   3.335 -
   3.336 -	// remove authorization token
   3.337 -	if data.PreviousAuthorizeData != nil {
   3.338 -		err = ctx.Tokens.RemoveAuthorization(data.PreviousAuthorizeData.Code)
   3.339 -		if err != nil && ctx.Log != nil {
   3.340 -			ctx.Log.Printf("Error removing previous auth data (%s): %s\n", data.PreviousAuthorizeData.Code, err)
   3.341 -		}
   3.342 -	}
   3.343 -
   3.344 -	// remove previous access token
   3.345 -	if data.PreviousAccessData != nil {
   3.346 -		if data.PreviousAccessData.RefreshToken != "" {
   3.347 -			err = ctx.Tokens.RemoveRefresh(data.PreviousAccessData.RefreshToken)
   3.348 -			if err != nil && ctx.Log != nil {
   3.349 -				ctx.Log.Printf("Error removing previous refresh token (%s): %s\n", data.PreviousAccessData.RefreshToken, err)
   3.350 -			}
   3.351 -		}
   3.352 -		err = ctx.Tokens.RemoveAccess(data.PreviousAccessData.AccessToken)
   3.353 -		if err != nil && ctx.Log != nil {
   3.354 -			ctx.Log.Printf("Error removing previous access token (%s): %s\n", data.PreviousAccessData.AccessToken, err)
   3.355 -		}
   3.356 -	}
   3.357 -
   3.358 -	data.TokenType = ctx.Config.TokenType
   3.359 -	data.ExpiresIn = ctx.Config.AccessExpiration
   3.360 -	data.CreatedAt = time.Now()
   3.361 -	return nil
   3.362 -}
   3.363 -
   3.364 -func (data AccessData) GetRedirect(fragment bool) (string, error) {
   3.365 -	u, err := url.Parse(data.RedirectURI)
   3.366 -	if err != nil {
   3.367 -		return "", err
   3.368 -	}
   3.369 -
   3.370 -	// add parameters
   3.371 -	q := u.Query()
   3.372 -	q.Set("access_token", data.AccessToken)
   3.373 -	q.Set("token_type", data.TokenType)
   3.374 -	q.Set("expires_in", strconv.FormatInt(int64(data.ExpiresIn), 10))
   3.375 -	if data.RefreshToken != "" {
   3.376 -		q.Set("refresh_token", data.RefreshToken)
   3.377 -	}
   3.378 -	if data.Scope != "" {
   3.379 -		q.Set("scope", data.Scope)
   3.380 -	}
   3.381 -	if len(data.ProfileID) > 0 {
   3.382 -		q.Set("profile", data.ProfileID.String())
   3.383 -	}
   3.384 -	if fragment {
   3.385 -		u.RawQuery = ""
   3.386 -		u.Fragment = q.Encode()
   3.387 -	} else {
   3.388 -		u.RawQuery = q.Encode()
   3.389 -	}
   3.390 -
   3.391 -	return u.String(), nil
   3.392 -}
   3.393 -
   3.394 -// getClient looks up and authenticates the basic auth using the given
   3.395 -// storage. Sets an error on the response if auth fails or a server error occurs.
   3.396 -func getClient(auth BasicAuth, ctx Context) (Client, error) {
   3.397 -	id, err := uuid.Parse(auth.Username)
   3.398 -	if err != nil {
   3.399 -		return Client{}, err
   3.400 -	}
   3.401 -	client, err := ctx.Clients.GetClient(id)
   3.402 -	if err != nil {
   3.403 -		if err == ClientNotFoundError {
   3.404 -			return Client{}, err
   3.405 -		}
   3.406 -		if ctx.Log != nil {
   3.407 -			ctx.Log.Printf("Error retrieving client %s: %s", id, err)
   3.408 -		}
   3.409 -		return Client{}, InternalServerError
   3.410 -	}
   3.411 -	if client.Secret != auth.Password {
   3.412 -		return Client{}, InvalidClientError
   3.413 -	}
   3.414 -	if client.RedirectURI == "" {
   3.415 -		return Client{}, InvalidClientError
   3.416 -	}
   3.417 -	return client, nil
   3.418 -}
     4.1 --- a/authorize.go.old	Mon Sep 01 09:49:23 2014 -0400
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,272 +0,0 @@
     4.4 -package auth
     4.5 -
     4.6 -import (
     4.7 -	"net/http"
     4.8 -	"net/url"
     4.9 -	"time"
    4.10 -
    4.11 -	"strings"
    4.12 -	"secondbit.org/uuid"
    4.13 -)
    4.14 -
    4.15 -// AuthorizeRequestType is the type for OAuth param `response_type`
    4.16 -type AuthorizeRequestType string
    4.17 -
    4.18 -const (
    4.19 -	CodeAuthRT  AuthorizeRequestType = "code"
    4.20 -	TokenAuthRT                      = "token"
    4.21 -)
    4.22 -
    4.23 -type AuthRequest struct {
    4.24 -	Client      Client
    4.25 -	Scope       string
    4.26 -	RedirectURI string
    4.27 -	State       string
    4.28 -}
    4.29 -
    4.30 -// Authorization data
    4.31 -type AuthorizeData struct {
    4.32 -	// Authorization code
    4.33 -	Code string
    4.34 -
    4.35 -	// Token expiration in seconds
    4.36 -	ExpiresIn int32
    4.37 -
    4.38 -	// Date created
    4.39 -	CreatedAt time.Time
    4.40 -
    4.41 -	ProfileID uuid.ID
    4.42 -
    4.43 -	AuthRequest
    4.44 -}
    4.45 -
    4.46 -// IsExpired is true if authorization expired
    4.47 -func (d *AuthorizeData) IsExpired() bool {
    4.48 -	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now())
    4.49 -}
    4.50 -
    4.51 -// ExpireAt returns the expiration date
    4.52 -func (d *AuthorizeData) ExpireAt() time.Time {
    4.53 -	return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second)
    4.54 -}
    4.55 -
    4.56 -// HandleAuthorizeRequest is the main http.HandlerFunc for handling
    4.57 -// authorization requests
    4.58 -func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
    4.59 -	r.ParseForm()
    4.60 -	// create the authorization request
    4.61 -	redirectURI := r.Form.Get("redirect_uri")
    4.62 -	var err error
    4.63 -	if redirectURI != "" {
    4.64 -		redirectURI, err = url.QueryUnescape(redirectURI)
    4.65 -		if err != nil {
    4.66 -			ctx.RenderError(w, URIFormatError(redirectURI))
    4.67 -			return
    4.68 -		}
    4.69 -	}
    4.70 -
    4.71 -	state := r.Form.Get("state")
    4.72 -	scope := r.Form.Get("scope")
    4.73 -
    4.74 -	// must have a valid client
    4.75 -	id, err := uuid.Parse(r.Form.Get("client_id"))
    4.76 -	if err != nil {
    4.77 -		ctx.RenderError(w, InvalidClientIDError(r.Form.Get("client_id")))
    4.78 -		return
    4.79 -	}
    4.80 -	client, err := GetClient(id, ctx)
    4.81 -	if err != nil {
    4.82 -		if err == ClientNotFoundError {
    4.83 -			ctx.RenderError(w, ClientNotFoundError)
    4.84 -			return
    4.85 -		}
    4.86 -		if redirectURI == "" {
    4.87 -			ctx.RenderError(w, URIMissingError)
    4.88 -			return
    4.89 -		}
    4.90 -		req := AuthRequest{
    4.91 -			RedirectURI: redirectURI,
    4.92 -			Scope:       scope,
    4.93 -			State:       state,
    4.94 -		}
    4.95 -		redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
    4.96 -		if err != nil {
    4.97 -			ctx.RenderError(w, URIFormatError(redirectURI))
    4.98 -			return
    4.99 -		}
   4.100 -		http.Redirect(w, r, redir, http.StatusFound)
   4.101 -		return
   4.102 -	}
   4.103 -	if client.RedirectURI == "" {
   4.104 -		ctx.RenderError(w, URIMissingError)
   4.105 -		return
   4.106 -	}
   4.107 -
   4.108 -	// check redirect uri
   4.109 -	if redirectURI == "" {
   4.110 -		redirectURI = client.RedirectURI
   4.111 -	}
   4.112 -	if err = validateURI(client.RedirectURI, redirectURI); err != nil {
   4.113 -		ctx.RenderError(w, NewURIMismatchError(client.RedirectURI, redirectURI))
   4.114 -		return
   4.115 -	}
   4.116 -
   4.117 -	req := AuthRequest{
   4.118 -		Client:      client,
   4.119 -		RedirectURI: redirectURI,
   4.120 -		Scope:       scope,
   4.121 -		State:       state,
   4.122 -	}
   4.123 -
   4.124 -	requestType := AuthorizeRequestType(r.Form.Get("response_type"))
   4.125 -	if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) {
   4.126 -		switch requestType {
   4.127 -		case CodeAuthRT:
   4.128 -			req.handleCodeRequest(w, r, ctx)
   4.129 -			return
   4.130 -		case TokenAuthRT:
   4.131 -			req.handleTokenRequest(w, r, ctx)
   4.132 -			return
   4.133 -		}
   4.134 -	}
   4.135 -	redir, err := req.GetErrorRedirect(ErrorInvalidRequest, "Invalid response type.", ctx.Config.DocumentationDomain)
   4.136 -	if err != nil {
   4.137 -		ctx.RenderError(w, URIFormatError(req.RedirectURI))
   4.138 -		return
   4.139 -	}
   4.140 -	http.Redirect(w, r, redir, http.StatusFound)
   4.141 -}
   4.142 -
   4.143 -func (req AuthRequest) handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
   4.144 -	if r.Method != "GET" && r.Method != "POST" {
   4.145 -		ctx.RenderError(w, InvalidMethodError)
   4.146 -		return
   4.147 -	}
   4.148 -
   4.149 -	if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
   4.150 -		http.Redirect(w, r, "/auth/login?redirect_to="+url.QueryEscape(r.URL.String()), http.StatusFound)
   4.151 -		return
   4.152 -	} else if err != nil {
   4.153 -		ctx.RenderError(w, err)
   4.154 -		return
   4.155 -	}
   4.156 -
   4.157 -	if r.Method == "GET" {
   4.158 -		ctx.RenderConfirmation(w, r, req)
   4.159 -		return
   4.160 -	}
   4.161 -
   4.162 -	if r.FormValue("approved") != "true" {
   4.163 -		redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
   4.164 -		if err != nil {
   4.165 -			ctx.RenderError(w, URIFormatError(req.RedirectURI))
   4.166 -			return
   4.167 -		}
   4.168 -		http.Redirect(w, r, redir, http.StatusFound)
   4.169 -		return
   4.170 -	}
   4.171 -
   4.172 -	data := AuthorizeData{AuthRequest: req}
   4.173 -
   4.174 -	data.ExpiresIn = ctx.Config.AuthorizationExpiration
   4.175 -	data.Code = newToken()
   4.176 -	data.CreatedAt = time.Now()
   4.177 -
   4.178 -	err := ctx.Tokens.SaveAuthorization(data)
   4.179 -	if err != nil {
   4.180 -		redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain)
   4.181 -		if err != nil {
   4.182 -			ctx.RenderError(w, URIFormatError(req.RedirectURI))
   4.183 -			return
   4.184 -		}
   4.185 -		http.Redirect(w, r, redir, http.StatusFound)
   4.186 -		return
   4.187 -	}
   4.188 -
   4.189 -	redir, err := data.GetRedirect()
   4.190 -	if err != nil {
   4.191 -		ctx.RenderError(w, URIFormatError(req.RedirectURI))
   4.192 -		return
   4.193 -	}
   4.194 -	http.Redirect(w, r, redir, http.StatusFound)
   4.195 -}
   4.196 -
   4.197 -func (req AuthRequest) handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
   4.198 -	if r.Method != "GET" && r.Method != "POST" {
   4.199 -		ctx.RenderError(w, InvalidMethodError)
   4.200 -		return
   4.201 -	}
   4.202 -
   4.203 -	if err := validateSession(r, ctx); err == ErrorNotAuthenticated {
   4.204 -		http.Redirect(w, r, "/auth/login?redirect_to="+url.QueryEscape(r.URL.String()), http.StatusFound)
   4.205 -		return
   4.206 -	} else if err != nil {
   4.207 -		ctx.RenderError(w, err)
   4.208 -		return
   4.209 -	}
   4.210 -
   4.211 -	if r.Method == "GET" {
   4.212 -		ctx.RenderConfirmation(w, r, req)
   4.213 -		return
   4.214 -	}
   4.215 -
   4.216 -	if r.FormValue("approved") != "true" {
   4.217 -		redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain)
   4.218 -		if err != nil {
   4.219 -			ctx.RenderError(w, URIFormatError(req.RedirectURI))
   4.220 -			return
   4.221 -		}
   4.222 -		http.Redirect(w, r, redir, http.StatusFound)
   4.223 -		return
   4.224 -	}
   4.225 -
   4.226 -	data := AccessData{AuthRequest: req}
   4.227 -
   4.228 -	err := fillTokens(&data, false, ctx)
   4.229 -	if err != nil {
   4.230 -		ctx.RenderError(w, InternalServerError)
   4.231 -		return
   4.232 -	}
   4.233 -
   4.234 -	redir, err := data.GetRedirect(true)
   4.235 -	if err != nil {
   4.236 -		ctx.RenderError(w, URIFormatError(req.RedirectURI))
   4.237 -		return
   4.238 -	}
   4.239 -	http.Redirect(w, r, redir, http.StatusFound)
   4.240 -}
   4.241 -
   4.242 -func (data AuthorizeData) GetRedirect() (string, error) {
   4.243 -	u, err := url.Parse(data.RedirectURI)
   4.244 -	if err != nil {
   4.245 -		return "", err
   4.246 -	}
   4.247 -
   4.248 -	// add parameters
   4.249 -	q := u.Query()
   4.250 -	q.Set("code", data.Code)
   4.251 -	q.Set("state", data.State)
   4.252 -	u.RawQuery = q.Encode()
   4.253 -
   4.254 -	return u.String(), nil
   4.255 -}
   4.256 -
   4.257 -func (req AuthRequest) GetErrorRedirect(code, description, uriBase string) (string, error) {
   4.258 -	u, err := url.Parse(req.RedirectURI)
   4.259 -	if err != nil {
   4.260 -		return "", err
   4.261 -	}
   4.262 -
   4.263 -	// add parameters
   4.264 -	q := u.Query()
   4.265 -	q.Set("error", code)
   4.266 -	q.Set("error_description", description)
   4.267 -	q.Set("error_uri", strings.Join([]string{
   4.268 -		strings.TrimRight(uriBase, "/"),
   4.269 -		strings.TrimLeft(code, "/"),
   4.270 -	}, "/"))
   4.271 -	q.Set("state", req.State)
   4.272 -	u.RawQuery = q.Encode()
   4.273 -
   4.274 -	return u.String(), nil
   4.275 -}
     5.1 --- a/config.go.old	Mon Sep 01 09:49:23 2014 -0400
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,79 +0,0 @@
     5.4 -package auth
     5.5 -
     5.6 -import "time"
     5.7 -
     5.8 -// AllowedAuthorizeType is a collection of allowed auth request types
     5.9 -type AllowedAuthorizeType []AuthorizeRequestType
    5.10 -
    5.11 -// Exists returns true if the auth type exists in the list
    5.12 -func (t AllowedAuthorizeType) Exists(rt AuthorizeRequestType) bool {
    5.13 -	for _, k := range t {
    5.14 -		if k == rt {
    5.15 -			return true
    5.16 -		}
    5.17 -	}
    5.18 -	return false
    5.19 -}
    5.20 -
    5.21 -// AllowedAccessType is a collection of allowed access request types
    5.22 -type AllowedAccessType []GrantType
    5.23 -
    5.24 -// Exists returns true if the access type exists in the list
    5.25 -func (t AllowedAccessType) Exists(rt GrantType) bool {
    5.26 -	for _, k := range t {
    5.27 -		if k == rt {
    5.28 -			return true
    5.29 -		}
    5.30 -	}
    5.31 -	return false
    5.32 -}
    5.33 -
    5.34 -// ServerConfig contains server configuration information
    5.35 -type ServerConfig struct {
    5.36 -	// Authorization token expiration in seconds (default 5 minutes)
    5.37 -	AuthorizationExpiration int32
    5.38 -
    5.39 -	// Access token expiration in seconds (default 1 hour)
    5.40 -	AccessExpiration int32
    5.41 -
    5.42 -	// Token type to return
    5.43 -	TokenType string
    5.44 -
    5.45 -	// List of allowed authorize types (only CodeAuthRT by default)
    5.46 -	AllowedAuthorizeTypes AllowedAuthorizeType
    5.47 -
    5.48 -	// List of allowed access types (only AUTHORIZATION_CodeAuthRT by default)
    5.49 -	AllowedAccessTypes AllowedAccessType
    5.50 -
    5.51 -	// HTTP status code to return for errors - default 200
    5.52 -	// Only used if response was created from server
    5.53 -	ErrorStatusCode int
    5.54 -
    5.55 -	// If true allows client secret also in params, else only in
    5.56 -	// Authorization header - default false
    5.57 -	AllowClientSecretInParams bool
    5.58 -
    5.59 -	// If true allows access request using GET, else only POST - default false
    5.60 -	AllowGetAccessRequest bool
    5.61 -
    5.62 -	// The base path of documentation
    5.63 -	DocumentationDomain string
    5.64 -
    5.65 -	SessionLength       time.Duration
    5.66 -	RequestIPHeader     string
    5.67 -	LoginRedirectDomain string
    5.68 -}
    5.69 -
    5.70 -// NewServerConfig returns a new ServerConfig with default configuration
    5.71 -func NewServerConfig() ServerConfig {
    5.72 -	return ServerConfig{
    5.73 -		AuthorizationExpiration:   250,
    5.74 -		AccessExpiration:          3600,
    5.75 -		TokenType:                 "bearer",
    5.76 -		AllowedAuthorizeTypes:     AllowedAuthorizeType{CodeAuthRT},
    5.77 -		AllowedAccessTypes:        AllowedAccessType{AuthorizationCodeGrant},
    5.78 -		ErrorStatusCode:           200,
    5.79 -		AllowClientSecretInParams: false,
    5.80 -		AllowGetAccessRequest:     false,
    5.81 -	}
    5.82 -}
     6.1 --- a/context.go.old	Mon Sep 01 09:49:23 2014 -0400
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,108 +0,0 @@
     6.4 -package auth
     6.5 -
     6.6 -import (
     6.7 -	"encoding/json"
     6.8 -	"html/template"
     6.9 -	"io"
    6.10 -	"log"
    6.11 -	"net/http"
    6.12 -
    6.13 -	"github.com/justinas/nosurf"
    6.14 -)
    6.15 -
    6.16 -type Context struct {
    6.17 -	Config    ServerConfig
    6.18 -	Clients   ClientStore
    6.19 -	Tokens    TokenStore
    6.20 -	Profiles  ProfileStore
    6.21 -	Sessions  SessionStore
    6.22 -	Log       *log.Logger
    6.23 -	Templates Templates
    6.24 -}
    6.25 -
    6.26 -type Templates struct {
    6.27 -	Error        *template.Template
    6.28 -	Confirmation *template.Template
    6.29 -	Login        *template.Template
    6.30 -}
    6.31 -
    6.32 -type jsonError struct {
    6.33 -	Error       string `json:"error,omitempty"`
    6.34 -	Description string `json:"error_description,omitempty"`
    6.35 -	URI         string `json:"error_uri,omitempty"`
    6.36 -	State       string `json:"state,omitempty"`
    6.37 -}
    6.38 -
    6.39 -func (c Context) RenderError(w io.Writer, err error) {
    6.40 -	if c.Templates.Error == nil {
    6.41 -		log.Println("Error template is nil, can't render error.")
    6.42 -		return
    6.43 -	}
    6.44 -	renderErr := c.Templates.Error.Execute(w, map[string]interface{}{
    6.45 -		"err": err,
    6.46 -	})
    6.47 -	if renderErr != nil {
    6.48 -		log.Printf("Error executing error template (oh, the irony): %s\n", renderErr)
    6.49 -		return
    6.50 -	}
    6.51 -}
    6.52 -
    6.53 -func (c Context) RenderJSONError(w io.Writer, code, description, baseURI string) {
    6.54 -	d, err := json.Marshal(jsonError{
    6.55 -		Error:       code,
    6.56 -		Description: description,
    6.57 -		URI:         baseURI,
    6.58 -	})
    6.59 -	if err != nil {
    6.60 -		log.Printf("Error marshalling json error (oh, the irony): %s\n", err)
    6.61 -		return
    6.62 -	}
    6.63 -	_, err = w.Write(d)
    6.64 -	if err != nil {
    6.65 -		log.Printf("Error writing json error: %s\n", err)
    6.66 -		return
    6.67 -	}
    6.68 -}
    6.69 -
    6.70 -func (c Context) RenderConfirmation(w io.Writer, r *http.Request, req AuthRequest) {
    6.71 -	if c.Templates.Confirmation == nil {
    6.72 -		log.Println("Confirmation template is nil, can't render confirmation.")
    6.73 -		return
    6.74 -	}
    6.75 -	err := c.Templates.Confirmation.Execute(w, map[string]interface{}{
    6.76 -		"scope":      req.Scope,
    6.77 -		"client":     req.Client,
    6.78 -		"csrf_token": nosurf.Token(r),
    6.79 -	})
    6.80 -	if err != nil {
    6.81 -		log.Printf("Error executing confirmation template: %s\n", err)
    6.82 -		return
    6.83 -	}
    6.84 -}
    6.85 -
    6.86 -func (c Context) RenderLogin(w io.Writer, r *http.Request) {
    6.87 -	if c.Templates.Login == nil {
    6.88 -		log.Println("Login template is nil, can't render confirmation.")
    6.89 -		return
    6.90 -	}
    6.91 -	err := c.Templates.Login.Execute(w, map[string]interface{}{
    6.92 -		"csrf_token": nosurf.Token(r),
    6.93 -	})
    6.94 -	if err != nil {
    6.95 -		log.Printf("Error executing login template: %s\n", err)
    6.96 -		return
    6.97 -	}
    6.98 -}
    6.99 -
   6.100 -func (c Context) RenderJSONToken(w io.Writer, data AccessData) {
   6.101 -	d, err := json.Marshal(data)
   6.102 -	if err != nil {
   6.103 -		log.Printf("Error marshalling json token: %s\n", err)
   6.104 -		return
   6.105 -	}
   6.106 -	_, err = w.Write(d)
   6.107 -	if err != nil {
   6.108 -		log.Printf("Error writing json token: %s\n", err)
   6.109 -		return
   6.110 -	}
   6.111 -}
     7.1 --- a/errors.go.old	Mon Sep 01 09:49:23 2014 -0400
     7.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3 @@ -1,55 +0,0 @@
     7.4 -package auth
     7.5 -
     7.6 -import "errors"
     7.7 -
     7.8 -const (
     7.9 -	ErrorServerError          = "server_error"
    7.10 -	ErrorInvalidRequest       = "invalid_request"
    7.11 -	ErrorAccessDenied         = "access_denied"
    7.12 -	ErrorInvalidClient        = "invalid_client"
    7.13 -	ErrorInvalidGrant         = "invalid_grant"
    7.14 -	ErrorUnauthorizedClient   = "unauthorized_client"
    7.15 -	ErrorUnsupportedGrantType = "unsupported_grant_type"
    7.16 -	ErrorInvalidScope         = "invalid_scope"
    7.17 -)
    7.18 -
    7.19 -var (
    7.20 -	ClientNotFoundError        = errors.New("Client not found.")
    7.21 -	URIMissingError            = errors.New("Redirect URI missing.")
    7.22 -	InvalidMethodError         = errors.New("Invalid request method.")
    7.23 -	InternalServerError        = errors.New("Internal server error.")
    7.24 -	ErrorNotAuthenticated      = errors.New("Not authenticated.")
    7.25 -	InvalidClientError         = errors.New("Invalid client.")
    7.26 -	AuthorizationNotFoundError = errors.New("Authorization not found.")
    7.27 -	ErrProfileNotFound         = errors.New("Profile not found.")
    7.28 -	TokenNotFoundError         = errors.New("Token not found.")
    7.29 -	NilClientError             = errors.New("Client was nil.")
    7.30 -)
    7.31 -
    7.32 -type URIFormatError string
    7.33 -
    7.34 -func (err URIFormatError) Error() string {
    7.35 -	return "Invalid URI format: " + string(err)
    7.36 -}
    7.37 -
    7.38 -type InvalidClientIDError string
    7.39 -
    7.40 -func (err InvalidClientIDError) Error() string {
    7.41 -	return "Invalid client ID: " + string(err)
    7.42 -}
    7.43 -
    7.44 -type URIMismatchError struct {
    7.45 -	uri      string
    7.46 -	mismatch string
    7.47 -}
    7.48 -
    7.49 -func (err URIMismatchError) Error() string {
    7.50 -	return "Supplied redirect URI " + err.mismatch + " does not match the redirect in the database (" + err.uri + ")"
    7.51 -}
    7.52 -
    7.53 -func NewURIMismatchError(uri, mismatch string) error {
    7.54 -	return URIMismatchError{
    7.55 -		uri:      uri,
    7.56 -		mismatch: mismatch,
    7.57 -	}
    7.58 -}
     8.1 --- a/session.go.old	Mon Sep 01 09:49:23 2014 -0400
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,101 +0,0 @@
     8.4 -package auth
     8.5 -
     8.6 -import (
     8.7 -	"errors"
     8.8 -	"net/http"
     8.9 -	"net/url"
    8.10 -	"time"
    8.11 -
    8.12 -	"strings"
    8.13 -	"secondbit.org/uuid"
    8.14 -)
    8.15 -
    8.16 -const sessionCookie = "session"
    8.17 -
    8.18 -var (
    8.19 -	ErrSessionNotFound = errors.New("Session not found.")
    8.20 -)
    8.21 -
    8.22 -type Session struct {
    8.23 -	Token   string
    8.24 -	Profile uuid.ID
    8.25 -	Expires time.Time
    8.26 -	Created time.Time
    8.27 -	IP      string
    8.28 -}
    8.29 -
    8.30 -type SessionStore interface {
    8.31 -	GetSession(token string) (Session, error)
    8.32 -	GetAllSessions(userID uuid.ID, num, page int) ([]Session, error)
    8.33 -	SaveSession(session Session) error
    8.34 -	DeleteSession(sessionID uuid.ID) error
    8.35 -}
    8.36 -
    8.37 -func validateSession(r *http.Request, c Context) error {
    8.38 -	cookie, err := r.Cookie(sessionCookie)
    8.39 -	if err == http.ErrNoCookie {
    8.40 -		return ErrSessionNotFound
    8.41 -	}
    8.42 -	_, err = c.Sessions.GetSession(cookie.Value)
    8.43 -	return err
    8.44 -}
    8.45 -
    8.46 -func HandleLoginRequest(w http.ResponseWriter, r *http.Request, ctx Context) {
    8.47 -	if r.Method == "GET" {
    8.48 -		ctx.RenderLogin(w, r)
    8.49 -		return
    8.50 -	} else if r.Method != "POST" {
    8.51 -		// TODO: return bad method error
    8.52 -		return
    8.53 -	}
    8.54 -
    8.55 -	if r.FormValue("username") == "" || r.FormValue("password") == "" {
    8.56 -		// TODO: return unauthenticated error
    8.57 -		return
    8.58 -	}
    8.59 -	id, err := ctx.Profiles.GetProfile(r.FormValue("username"), r.FormValue("password"))
    8.60 -	if err != nil {
    8.61 -		if err == ErrProfileNotFound {
    8.62 -			// TODO: return unauthenticated error
    8.63 -			return
    8.64 -		}
    8.65 -		// TODO: return internal server error
    8.66 -		return
    8.67 -	}
    8.68 -	session := Session{
    8.69 -		Token:   newToken(),
    8.70 -		Profile: id,
    8.71 -		Expires: time.Now().Add(ctx.Config.SessionLength),
    8.72 -		Created: time.Now(),
    8.73 -		IP:      r.Header.Get(ctx.Config.RequestIPHeader),
    8.74 -	}
    8.75 -	err = ctx.Sessions.SetSession(session)
    8.76 -	if err != nil {
    8.77 -		// TODO: return internal server error
    8.78 -		return
    8.79 -	}
    8.80 -	http.SetCookie(w, &http.Cookie{
    8.81 -		Name:     sessionCookie,
    8.82 -		Value:    session.Token,
    8.83 -		Expires:  session.Expires,
    8.84 -		Secure:   true,
    8.85 -		HttpOnly: true,
    8.86 -	})
    8.87 -
    8.88 -	redirectString := r.URL.Query().Get("redirect_to")
    8.89 -	if redirectString != "" {
    8.90 -		redirectURI, err := url.Parse(redirectString)
    8.91 -		if err != nil {
    8.92 -			// TODO: render a bad request error
    8.93 -			return
    8.94 -		}
    8.95 -		if !strings.HasSuffix("."+ctx.Config.LoginRedirectDomain, redirectURI.Host) && redirectURI.Host != ctx.Config.LoginRedirectDomain {
    8.96 -			// TODO: render a bad request error
    8.97 -			return
    8.98 -		}
    8.99 -	} else {
   8.100 -		redirectString = "https://" + ctx.Config.LoginRedirectDomain
   8.101 -	}
   8.102 -	http.Redirect(w, r, redirectString, http.StatusFound)
   8.103 -	return
   8.104 -}
     9.1 --- a/util.go.old	Mon Sep 01 09:49:23 2014 -0400
     9.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.3 @@ -1,102 +0,0 @@
     9.4 -package auth
     9.5 -
     9.6 -import (
     9.7 -	"encoding/base64"
     9.8 -	"errors"
     9.9 -	"fmt"
    9.10 -	"net/http"
    9.11 -	"net/url"
    9.12 -	"strings"
    9.13 -
    9.14 -	"code.google.com/p/go-uuid/uuid"
    9.15 -)
    9.16 -
    9.17 -var (
    9.18 -	BasicAuthNotSetError      = errors.New("Authorization header not set.")
    9.19 -	InvalidBasicAuthTypeError = errors.New("Invalid basic auth type.")
    9.20 -	InvalidBasicAuthMessage   = errors.New("Invalid basic auth format.")
    9.21 -)
    9.22 -
    9.23 -// Parse basic authentication header
    9.24 -type BasicAuth struct {
    9.25 -	Username string
    9.26 -	Password string
    9.27 -}
    9.28 -
    9.29 -// Return authorization header data
    9.30 -func CheckBasicAuth(r *http.Request) (BasicAuth, error) {
    9.31 -	if r.Header.Get("Authorization") == "" {
    9.32 -		return BasicAuth{}, BasicAuthNotSetError
    9.33 -	}
    9.34 -
    9.35 -	s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
    9.36 -	if len(s) != 2 || s[0] != "Basic" {
    9.37 -		return BasicAuth{}, InvalidBasicAuthTypeError
    9.38 -	}
    9.39 -
    9.40 -	b, err := base64.StdEncoding.DecodeString(s[1])
    9.41 -	if err != nil {
    9.42 -		return BasicAuth{}, err
    9.43 -	}
    9.44 -	pair := strings.SplitN(string(b), ":", 2)
    9.45 -	if len(pair) != 2 {
    9.46 -		return BasicAuth{}, InvalidBasicAuthMessage
    9.47 -	}
    9.48 -
    9.49 -	return BasicAuth{Username: pair[0], Password: pair[1]}, nil
    9.50 -}
    9.51 -
    9.52 -// getClientAuth checks client basic authentication in params if allowed,
    9.53 -// otherwise gets it from the header.
    9.54 -func getClientAuth(r *http.Request, allowQueryParams bool) (BasicAuth, error) {
    9.55 -
    9.56 -	if allowQueryParams {
    9.57 -		// Allow for auth without password
    9.58 -		if _, hasSecret := r.Form["client_secret"]; hasSecret {
    9.59 -			auth := BasicAuth{
    9.60 -				Username: r.Form.Get("client_id"),
    9.61 -				Password: r.Form.Get("client_secret"),
    9.62 -			}
    9.63 -			if auth.Username != "" {
    9.64 -				return auth, nil
    9.65 -			}
    9.66 -		}
    9.67 -	}
    9.68 -
    9.69 -	return CheckBasicAuth(r)
    9.70 -}
    9.71 -
    9.72 -func newToken() string {
    9.73 -	return base64.StdEncoding.EncodeToString([]byte(uuid.New()))
    9.74 -}
    9.75 -
    9.76 -// validateURI validates that redirectURI is contained in baseURI
    9.77 -func validateURI(baseURI string, redirectURI string) error {
    9.78 -	if baseURI == "" || redirectURI == "" {
    9.79 -		return errors.New("urls cannot be blank.")
    9.80 -	}
    9.81 -
    9.82 -	// parse base url
    9.83 -	base, err := url.Parse(baseURI)
    9.84 -	if err != nil {
    9.85 -		return err
    9.86 -	}
    9.87 -
    9.88 -	// parse passed url
    9.89 -	redirect, err := url.Parse(redirectURI)
    9.90 -	if err != nil {
    9.91 -		return err
    9.92 -	}
    9.93 -
    9.94 -	// must not have fragment
    9.95 -	if base.Fragment != "" || redirect.Fragment != "" {
    9.96 -		return errors.New("url must not include fragment.")
    9.97 -	}
    9.98 -
    9.99 -	// check if urls match
   9.100 -	if base.Scheme == redirect.Scheme && base.Host == redirect.Host && len(redirect.Path) >= len(base.Path) && strings.HasPrefix(redirect.Path, base.Path) {
   9.101 -		return nil
   9.102 -	}
   9.103 -
   9.104 -	return errors.New(fmt.Sprintf("urls don't validate: %s / %s\n", baseURI, redirectURI))
   9.105 -}