github.com/pbberlin/tools@v0.0.0-20160910141205-7aa5421c2169/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  	client       *gitkit.Client
    84  )
    85  
    86  // User information.
    87  type User struct {
    88  	ID            string
    89  	Email         string
    90  	Name          string
    91  	EmailVerified bool
    92  }
    93  
    94  // Key used to store the user information in the current session.
    95  type SessionUserKey int
    96  
    97  const sessionUserKey SessionUserKey = 0
    98  
    99  // SignedIn is on top of LoggedIn.
   100  // Can only be measured by presence of cookie gtokenCookieName
   101  func IsSignedIn(r *http.Request) bool {
   102  	signedIn := false
   103  	cks := r.Cookies()
   104  	for _, ck := range cks {
   105  		if ck.Name == gtokenCookieName {
   106  			signedIn = true
   107  			break
   108  		}
   109  	}
   110  	return signedIn
   111  }
   112  
   113  //
   114  // CurrentUser extracts the user information stored in current session.
   115  //
   116  // If there is no existing session, identity toolkit token is checked.
   117  // If the token is valid, a new session is created.
   118  //
   119  // If any error happens, nil is returned.
   120  func CurrentUser(r *http.Request) *User {
   121  	c := appengine.NewContext(r)
   122  	sess, _ := cookieStore.Get(r, sessionName)
   123  	if sess.IsNew {
   124  		// Create an identity toolkit client associated with the GAE context.
   125  		// client, err := gitkit.NewWithContext(c, gitkitClient)
   126  		// if err != nil {
   127  		// 	aelog.Errorf(c, "Failed to create a gitkit.Client with a context: %s", err)
   128  		// 	return nil
   129  		// }
   130  		// Extract the token string from request.
   131  		ts := client.TokenFromRequest(r)
   132  		if ts == "" {
   133  			return nil
   134  		}
   135  		// Check the token issue time. Only accept token that is no more than 15
   136  		// minutes old even if it's still valid.
   137  		// token, err := client.ValidateToken(ts)
   138  		token, err := client.ValidateToken(appengine.NewContext(r), ts, []string{clientID})
   139  		if err != nil {
   140  			aelog.Errorf(c, "Invalid token %s: %s", ts, err)
   141  			return nil
   142  		}
   143  		if time.Now().Sub(token.IssueAt) > maxTokenAge*time.Second {
   144  			aelog.Infof(c, "Token %s is too old. Issused at: %s", ts, token.IssueAt)
   145  			return nil
   146  		}
   147  
   148  		// Fetch user info.
   149  		u, err := client.UserByLocalID(token.LocalID)
   150  		if err != nil {
   151  			aelog.Errorf(c, "Failed to fetch user info for %s[%s]: %s", token.Email, token.LocalID, err)
   152  			return nil
   153  		}
   154  		return &User{
   155  			ID:            u.LocalID,
   156  			Email:         u.Email,
   157  			Name:          u.DisplayName,
   158  			EmailVerified: u.EmailVerified,
   159  		}
   160  	} else {
   161  		// Extracts user from current session.
   162  		v, ok := sess.Values[sessionUserKey]
   163  		if !ok {
   164  			aelog.Errorf(c, "no user found in current session")
   165  		}
   166  		return v.(*User)
   167  	}
   168  }
   169  
   170  // saveCurrentUser stores the user information in current session.
   171  func saveCurrentUser(r *http.Request, w http.ResponseWriter, u *User) {
   172  	if u == nil {
   173  		return
   174  	}
   175  	sess, _ := cookieStore.Get(r, sessionName)
   176  	sess.Values[sessionUserKey] = *u
   177  	err := sess.Save(r, w)
   178  	if err != nil {
   179  		aelog.Errorf(appengine.NewContext(r), "Cannot save session: %s", err)
   180  	}
   181  }