func handleSigninRequest(rw http.ResponseWriter, rq *http.Request) { reqVars := mux.Vars(rq) if siteId, ok := reqVars["site"]; !ok || len(siteId) == 0 { http.Error(rw, "Site ID must be provided", http.StatusBadRequest) return } else { wanted := site.Entity{ClientId: siteId} if err := wanted.Load(); err != nil { log.Println(err) if strings.Contains(err.Error(), "no rows") { http.Error(rw, "Site not found", http.StatusNotFound) } else { http.Error(rw, "Something broke", http.StatusInternalServerError) } return } if result, err := templates["signin.hbs"].Exec(wanted); err != nil { log.Println(err) http.Error(rw, "Something broke", http.StatusInternalServerError) return } else { rw.Write([]byte(result)) } } }
func handleSessionCreation(rw http.ResponseWriter, rq *http.Request) { reqVars := mux.Vars(rq) if rq.Method != "POST" { http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed) } else if siteId, ok := reqVars["site"]; !ok { http.Error(rw, "Site ID must be provided", http.StatusBadRequest) } else { wanted := site.Entity{ClientId: siteId} if err := wanted.Load(); err != nil { log.Println(err) if strings.Contains(err.Error(), "no rows") { http.Error(rw, "Site not found", http.StatusNotFound) } else { http.Error(rw, "Something broke", http.StatusInternalServerError) } return } if !cert.ClientVerified(rq) { http.Error(rw, "Client certificate not provided. Please restart your browser to provide it.", http.StatusBadRequest) } else { if userCert, err := cert.NewCertFromRequest(rq); err != nil { log.Println(err) http.Error(rw, "Failed to parse your client cert", http.StatusBadRequest) } else { if userEntity, err := user.FindOrCreate(userCert); err != nil { log.Println(err) http.Error(rw, "Something broke", http.StatusInternalServerError) } else { if sess, err := session.NewSession(&wanted, userEntity); err != nil { log.Println(err) http.Error(rw, "Something broke", http.StatusInternalServerError) } else { if callback, err := url.Parse(wanted.CallbackURL); err != nil { http.Error(rw, "Invalid callback URL provided", http.StatusInternalServerError) } else { //Indicate signin action with GET parameter q := callback.Query() q.Set("siil_action", "signin") callback.RawQuery = q.Encode() if t, err := templates["success.hbs"].Exec(map[string]string{"token": sess.Token, "callback": callback.String()}); err != nil { log.Println(err) http.Error(rw, "Something broke", http.StatusInternalServerError) } else { rw.Write([]byte(t)) } } } } } } } }
//Provide information about the active id card user func handleAPIMeRequest(rw http.ResponseWriter, rq *http.Request) { switch rq.Method { case "GET": if !cert.ClientVerified(rq) { http.Error(rw, "Certificate not provided", http.StatusBadRequest) } else { if c, err := cert.NewCertFromRequest(rq); err != nil { http.Error(rw, "Certificate not provided", http.StatusBadRequest) } else { if clientId := rq.FormValue("client_id"); len(clientId) == 0 { http.Error(rw, "Invalid client_id provided", http.StatusBadRequest) } else { //Check origin header validity if origin := rq.Header.Get("Origin"); len(origin) != 0 { if u, err := url.Parse(origin); err != nil { http.Error(rw, "Invalid origin provided", http.StatusBadRequest) return } else { s := site.Entity{Domain: u.Host} if err := s.Load(); err == nil && s.ClientId == clientId { rw.Header().Set("Access-Control-Allow-Origin", origin) rw.Header().Set("Access-Control-Allow-Methods", "GET") rw.Header().Set("Access-Control-Allow-Credentials", "true") } else { http.Error(rw, "Origin not allowed", http.StatusUnauthorized) return } } } s := site.Entity{ClientId: clientId} if err := s.Load(); err != nil { http.Error(rw, "Invalid client_id provided", http.StatusBadRequest) return } if usr, err := user.Find(c); err != nil { http.Error(rw, "Looks like we don't know you", http.StatusUnauthorized) } else { if s.HasActiveSessionFor(usr) { enc := json.NewEncoder(rw) rw.Header().Set("Content-Type", "application/json") if err := enc.Encode(usr); err != nil { log.Println(err) http.Error(rw, "Failed to compose response", http.StatusInternalServerError) } } else { http.Error(rw, "Computer says no", http.StatusUnauthorized) } } } } } default: http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed) } }
func checkSiteAndUserConnetion(rq *http.Request, siteId string) (*site.Entity, error) { owner, err := getOwnerFromSession(rq) if err != nil { return nil, err } s := site.Entity{ClientId: siteId} if err := s.Load(); err != nil { log.Println(err) return nil, errors.New("Failed to find site") } if s.Owner != owner { return nil, errors.New("Site owner doesn't match session owner.") } return &s, nil }
//Invalidate the session func handleSignoutRequest(rw http.ResponseWriter, rq *http.Request) { if rq.Method != "POST" { http.Error(rw, "Invalid method", http.StatusMethodNotAllowed) } else { reqVars := mux.Vars(rq) if token, ok := reqVars["token"]; !ok || len(token) != session.TOKEN_LENGTH { http.Error(rw, "Bad token provided", http.StatusBadRequest) } else { if sess, err := session.GetSession(token); err != nil { http.Error(rw, "Session not found", http.StatusUnauthorized) } else { if !cert.ClientVerified(rq) { http.Error(rw, "Cert not provided", http.StatusBadRequest) } else { if userCert, err := cert.NewCertFromRequest(rq); err != nil { log.Println(err) http.Error(rw, "Failed to parse your client cert", http.StatusBadRequest) } else { if usr, err := user.Find(userCert); err != nil { log.Println(err) http.Error(rw, "We don't know you", http.StatusUnauthorized) } else { if usr.Id == sess.UserId { if err := sess.Delete(); err != nil { http.Error(rw, "Failed to end session", http.StatusInternalServerError) } else { wanted := site.Entity{ClientId: sess.SiteId} if err := wanted.Load(); err != nil { log.Println(err) if strings.Contains(err.Error(), "no rows") { http.Error(rw, "Site not found", http.StatusNotFound) } else { http.Error(rw, "Something broke", http.StatusInternalServerError) } return } if callback, err := url.Parse(wanted.CallbackURL); err != nil { http.Error(rw, "Invalid callback URL provided", http.StatusInternalServerError) } else { //Indicate signin action with GET parameter q := callback.Query() q.Set("siil_action", "signout") callback.RawQuery = q.Encode() if t, err := templates["success.hbs"].Exec(map[string]string{"token": sess.Token, "callback": callback.String()}); err != nil { log.Println(err) http.Error(rw, "Something broke", http.StatusInternalServerError) } else { rw.Write([]byte(t)) } } } } else { http.Error(rw, "Session and user don't match", http.StatusBadRequest) } } } } } } } }
//Provide JSON encoded information about the user related to this session func handleAPISessionRequest(rw http.ResponseWriter, rq *http.Request) { if rq.Method != "GET" { http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed) } else { if token, clientId := rq.FormValue("token"), rq.FormValue("client_id"); len(token) != session.TOKEN_LENGTH || len(clientId) != site.CLIENT_ID_LENGTH { http.Error(rw, "Invalid request", http.StatusBadRequest) } else { if sess, err := session.GetSession(token); err != nil { log.Println(err) http.Error(rw, "Not found", http.StatusNotFound) } else { //Check the client id's if sess.SiteId != clientId { http.Error(rw, "Unauthorized", http.StatusUnauthorized) return } s := site.Entity{ClientId: sess.SiteId} if err := s.Load(); err != nil { log.Println(err) http.Error(rw, "Not found", http.StatusNotFound) return } //Lets check the X-Authorization header if sig := rq.Header.Get("X-Authorization"); len(sig) == 0 { http.Error(rw, "Unauthorized (Missing signature in X-Authorization)", http.StatusUnauthorized) } else { sigHMAC, err := base64.StdEncoding.DecodeString(sig) if err != nil { log.Println(err) http.Error(rw, "Invalid signature provided", http.StatusUnauthorized) return } ourMAC := hmac.New(sha512.New, []byte(s.PrivateKey)) ourMAC.Write([]byte(fmt.Sprintf("%s\t%s", clientId, token))) if !hmac.Equal(sigHMAC, ourMAC.Sum(nil)) { http.Error(rw, "Invalid signature provided", http.StatusUnauthorized) } else { if usr, err := user.FindById(sess.UserId); err != nil { log.Println(err) http.Error(rw, "Not found", http.StatusNotFound) } else { response := apiResponse{ Token: token, ExpiresAt: sess.ExpiresAt.Format(time.RFC3339), CreatedAt: sess.CreatedAt.Format(time.RFC3339), User: usr, Site: clientId, } //Generate verification HMAC verHMAC := hmac.New(sha512.New, []byte(s.PrivateKey)) if m, err := json.Marshal(response); err != nil { log.Println(err) http.Error(rw, "Failed to sign response", http.StatusInternalServerError) } else { verHMAC.Write(m) } response.Verification = base64.StdEncoding.EncodeToString(verHMAC.Sum(nil)) enc := json.NewEncoder(rw) rw.Header().Set("Content-Type", "application/json") if err := enc.Encode(response); err != nil { log.Println(err) http.Error(rw, "Failed to compose response", http.StatusInternalServerError) } } } } } } } }