github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/appengine/login/gitkit1/gitkit1.go (about)

     1  // package gitkit1 omits gitkit functions for updating user data or account-deletion;
     2  // instead all app-specific settings and the redirect-urls are customizable.
     3  package gitkit1
     4  
     5  // Code taken from
     6  // https://github.com/googlesamples/identity-toolkit-go/tree/master/favweekday
     7  //
     8  // The complete concept is expained here:
     9  // https://developers.google.com/identity/choose-auth
    10  // https://developers.google.com/identity/toolkit/web/federated-login
    11  //
    12  // https://developers.google.com/identity/toolkit/web/configure-service
    13  // https://developers.google.com/identity/toolkit/web/setup-frontend
    14  //
    15  //
    16  // Remove apps:
    17  // https://security.google.com/settings/security/permissions
    18  // https://www.facebook.com/settings?tab=applications
    19  
    20  import (
    21  	"net/http"
    22  	"time"
    23  
    24  	// issues certificates (tokens) for possible http requests, making other requests impossible
    25  
    26  	"github.com/google/identity-toolkit-go-client/gitkit"
    27  	"github.com/gorilla/sessions"
    28  
    29  	"google.golang.org/appengine"
    30  	aelog "google.golang.org/appengine/log"
    31  )
    32  
    33  // Action URLs.
    34  // These need to be updated
    35  // https://console.developers.google.com/project/tec-news/apiui/credential
    36  // https://console.developers.google.com/project/tec-news/apiui/apiview/identitytoolkit/identity_toolkit
    37  // https://developers.facebook.com/apps/942324259171809/settings/advanced/
    38  
    39  const (
    40  	homeURL = "/auth"
    41  
    42  	WidgetSigninAuthorizedRedirectURL = "/auth/authorized-redirect" // THIS one needs to be registered all over
    43  	signOutURL                        = "/auth/signout"
    44  	accountChooserBrandingURL         = "/auth/accountChooserBranding.html"
    45  
    46  	oobActionURL = "/auth/send-email" // not needed, but kept
    47  )
    48  
    49  var (
    50  	signinLandingDefaultURL  = "/auth/signin-default-landing"
    51  	signoutLandingDefaultURL = "/auth/signout-default-landing"
    52  	// successLandingURL = "/auth/signin-landing"
    53  	// signoutLandingURL = "/auth/signout-landing"
    54  )
    55  
    56  // Cookie/Form input names.
    57  const (
    58  
    59  	// contains jws from appengine/user.CurrentUser() ...;
    60  	// not used here
    61  	aeUserSessName = "SACSID"
    62  
    63  	// = cookie name;
    64  	// contains jwt from google/facebook/twitter;
    65  	// remains, even when "signed out"
    66  	// remains, even when logging out of google/twitter
    67  	// cannot be overwritten by "eraser"
    68  	sessionName = "SESSIONID"
    69  
    70  	// Created on top of sessionName on "signin"
    71  	// Remains
    72  	gtokenCookieName = "gtoken"
    73  
    74  	maxTokenAge = 1200 // 20 minutes
    75  
    76  	maxSessionIDAge = 1800
    77  )
    78  
    79  var (
    80  	xsrfKey      string
    81  	cookieStore  *sessions.CookieStore
    82  	gitkitClient *gitkit.Client
    83  )
    84  
    85  // User information.
    86  type User struct {
    87  	ID            string
    88  	Email         string
    89  	Name          string
    90  	EmailVerified bool
    91  }
    92  
    93  // Key used to store the user information in the current session.
    94  type SessionUserKey int
    95  
    96  const sessionUserKey SessionUserKey = 0
    97  
    98  // SignedIn is on top of LoggedIn.
    99  // Can only be measured by presence of cookie gtokenCookieName
   100  func IsSignedIn(r *http.Request) bool {
   101  	signedIn := false
   102  	cks := r.Cookies()
   103  	for _, ck := range cks {
   104  		if ck.Name == gtokenCookieName {
   105  			signedIn = true
   106  			break
   107  		}
   108  	}
   109  	return signedIn
   110  }
   111  
   112  //
   113  // CurrentUser extracts the user information stored in current session.
   114  //
   115  // If there is no existing session, identity toolkit token is checked.
   116  // If the token is valid, a new session is created.
   117  //
   118  // If any error happens, nil is returned.
   119  func CurrentUser(r *http.Request) *User {
   120  	c := appengine.NewContext(r)
   121  	sess, _ := cookieStore.Get(r, sessionName)
   122  	if sess.IsNew {
   123  		// Create an identity toolkit client associated with the GAE context.
   124  		client, err := gitkit.NewWithContext(c, gitkitClient)
   125  		if err != nil {
   126  			aelog.Errorf(c, "Failed to create a gitkit.Client with a context: %s", err)
   127  			return nil
   128  		}
   129  		// Extract the token string from request.
   130  		ts := client.TokenFromRequest(r)
   131  		if ts == "" {
   132  			return nil
   133  		}
   134  		// Check the token issue time. Only accept token that is no more than 15
   135  		// minitues old even if it's still valid.
   136  		token, err := client.ValidateToken(ts)
   137  		if err != nil {
   138  			aelog.Errorf(c, "Invalid token %s: %s", ts, err)
   139  			return nil
   140  		}
   141  		if time.Now().Sub(token.IssueAt) > maxTokenAge*time.Second {
   142  			aelog.Infof(c, "Token %s is too old. Issused at: %s", ts, token.IssueAt)
   143  			return nil
   144  		}
   145  
   146  		// Fetch user info.
   147  		u, err := client.UserByLocalID(token.LocalID)
   148  		if err != nil {
   149  			aelog.Errorf(c, "Failed to fetch user info for %s[%s]: %s", token.Email, token.LocalID, err)
   150  			return nil
   151  		}
   152  		return &User{
   153  			ID:            u.LocalID,
   154  			Email:         u.Email,
   155  			Name:          u.DisplayName,
   156  			EmailVerified: u.EmailVerified,
   157  		}
   158  	} else {
   159  		// Extracts user from current session.
   160  		v, ok := sess.Values[sessionUserKey]
   161  		if !ok {
   162  			aelog.Errorf(c, "no user found in current session")
   163  		}
   164  		return v.(*User)
   165  	}
   166  }
   167  
   168  // saveCurrentUser stores the user information in current session.
   169  func saveCurrentUser(r *http.Request, w http.ResponseWriter, u *User) {
   170  	if u == nil {
   171  		return
   172  	}
   173  	sess, _ := cookieStore.Get(r, sessionName)
   174  	sess.Values[sessionUserKey] = *u
   175  	err := sess.Save(r, w)
   176  	if err != nil {
   177  		aelog.Errorf(appengine.NewContext(r), "Cannot save session: %s", err)
   178  	}
   179  }