Waylon  Bruen

Waylon Bruen

1646844480

Dead Simple, Highly Performant, Highly Customizable Sessions Service

If you're interested in jwt's, see my jwt library!

Sessions

A dead simple, highly performant, highly customizable sessions service for go http servers.

By default, the service stores sessions in redis, and transports sessions to clients in cookies. However, these are easily customizeable. For instance, the storage interface only implements three methods:

// ServiceInterface defines the behavior of the session store
type ServiceInterface interface {
    SaveUserSession(userSession *user.Session) error
    DeleteUserSession(sessionID string) error
    FetchValidUserSession(sessionID string) (*user.Session, error)
}

README Contents:

  1. Quickstart
  2. Performance
  3. Design
  4. API
  5. Test Coverage
  6. Example
  7. License

Quickstart

var sesh *sessions.Service

// issue a new session and write the session to the ResponseWriter
userSession, err := sesh.IssueUserSession("fakeUserID", "{\"foo\":\"bar\"}", w)
if err != nil {
    log.Printf("Err issuing user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}

...

// Fetch a pointer to a valid user session from a request. A nil pointer indicates no or invalid session
userSession, err := sesh.GetUserSession(r)
if err != nil {
    log.Printf("Err fetching user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}
// nil session pointers indicate a 401 unauthorized
if userSession == nil {
    http.Error(w, "Unathorized", http.StatusUnauthorized)
    return
}

...

// Extend session expiry. Note that session expiry's need to be manually extended
if err := sesh.ExtendUserSession(userSession, r, w); err != nil {
    log.Printf("Err extending user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}

...

// Invalidate a user session, deleting it from redis and expiring the cookie on the ResponseWriter
if err := sesh.ClearUserSession(userSession, w); err != nil {
    log.Printf("Err clearing user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}

Performance

Benchmarks require a redis-server running. Set the REDIS_URL environment variable, otherwise the benchmarks look for ":6379".

YMMV

$ (cd benchmark && go test -bench=.)

setting up benchmark tests
BenchmarkBaseServer-2              20000             72479 ns/op
BenchmarkValidSession-2            10000            151650 ns/op
PASS
shutting down benchmark tests
ok      github.com/adam-hanna/sessions/benchmark        3.727s

Design

By default, the service stores sessions in redis, and transports hashed sessionIDs to clients in cookies. However, these are easily customizeable through the creation of custom structs that implement the interface.

The general flow of the session service is as follows:

  1. Create store, auth and transport services by calling their respective New(...) functions (or create your own custom services that implement the service's interface methods). Then pass these services to the sessions.New(...) constructor.
  2. After a user logs in, call the sessions.IssueUserSession(...) function. This function first creates a new user.Session. SessionIDs are RFC 4122 version 4 uuids. Next, the service hashes the sessionID with the provided key. The hashing algorithm is SHA-512, and therefore the key used should be between 64 and 128 bytes. Then, the service stores the session in redis and finally writes the hashed sessionID to the response writer in a cookie. Sessions written to the redis db utilize EXPIREAT to automatically destory expired sessions.
  3. To check if a valid session was included in a request, use the sessions.GetUserSession(...) function. This function grabs the hashed sessionID from the session cookie, verifies the HMAC signature and finally looks up the session in the redis db. If the session is expired, or fails HMAC signature verification, this function will return a nil pointer to a user session. If the session is valid, and you'd like to extend the session's expiry, you can then call session.ExtendUserSession(...). Session expiry's are never automatically extended, only through calling this function will the session's expiry be extended.
  4. When a user logs out, call the sessions.ClearUserSession(...) function. This function destroys the session in the db and also destroys the cookie on the ResponseWriter.

API

user.Session

type Session struct {
    ID        string
    UserID    string
    ExpiresAt time.Time
    JSON      string
}

Session is the struct that is used to store session data. The JSON field allows you to set any custom information you'd like. See the example

IssueUserSession

func (s *Service) IssueUserSession(userID string, json string, w http.ResponseWriter) (*user.Session, error)

IssueUserSession grants a new user session, writes that session info to the store and writes the session on the http.ResponseWriter.

This method should be called when a user logs in, for example.

ClearUserSession

func (s *Service) ClearUserSession(userSession *user.Session, w http.ResponseWriter) error

ClearUserSession is used to remove the user session from the store and clear the cookies on the ResponseWriter.

This method should be called when a user logs out, for example.

GetUserSession

func (s *Service) GetUserSession(r *http.Request) (*user.Session, error)

GetUserSession returns a user session from the hashed sessionID included in the request. This method only returns valid sessions. Therefore, sessions that have expired or that fail signature verification will return a nil pointer.

ExtendUserSession

func (s *Service) ExtendUserSession(userSession *user.Session, r *http.Request, w http.ResponseWriter) error

ExtendUserSession extends the ExpiresAt of a session by the Options.ExpirationDuration

Note that this function must be called, manually! Extension of user session expiry's does not happen automatically!

Testing Coverage

ok      github.com/adam-hanna/sessions            9.012s  coverage: 94.1% of statements
ok      github.com/adam-hanna/sessions/auth        0.003s  coverage: 100.0% of statements
ok      github.com/adam-hanna/sessions/store        0.006s  coverage: 85.4% of statements
ok      github.com/adam-hanna/sessions/benchmark    0.004s  coverage: 0.0% of statements [no tests to run]
ok      github.com/adam-hanna/sessions/transport    0.004s  coverage: 95.2% of statements
ok      github.com/adam-hanna/sessions/user        0.003s  coverage: 100.0% of statements

Tests are broken down into three categories: unit, integration and e2e. Integration and e2e tests require a connection to a redis server. The connection address can be set in the REDIS_URL environment variable. The default is ":6379".

To run all tests, simply:

$ go test -tags="unit integration e2e" ./...

// or
$ make test

// or
$ make test-cover-html && go tool cover -html=coverage-all.out

To run only tests from one of the categories:

$ go test -tags="integration" ./...

To run only unit and integration tests:

$ go test -tags="unit integration" ./...

Example

The following example is a demonstration of using the session service along with a CSRF code to check for authentication. The CSRF code is stored in the user.Session JSON field.

package main

import (
    "crypto/rand"
    "encoding/base64"
    "encoding/json"
    "io"
    "log"
    "net/http"
    "time"

    "github.com/adam-hanna/sessions"
    "github.com/adam-hanna/sessions/auth"
    "github.com/adam-hanna/sessions/store"
    "github.com/adam-hanna/sessions/transport"
)

// SessionJSON is used for marshalling and unmarshalling custom session json information.
// We're using it as an opportunity to tie csrf strings to sessions to prevent csrf attacks
type SessionJSON struct {
    CSRF string `json:"csrf"`
}

var sesh *sessions.Service

var issueSession = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    csrf, err := generateKey()
    if err != nil {
        log.Printf("Err generating csrf: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }

    myJSON := SessionJSON{
        CSRF: csrf,
    }
    JSONBytes, err := json.Marshal(myJSON)
    if err != nil {
        log.Printf("Err marhsalling json: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }

    userSession, err := sesh.IssueUserSession("fakeUserID", string(JSONBytes[:]), w)
    if err != nil {
        log.Printf("Err issuing user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    log.Printf("In issue; user's session: %v\n", userSession)

    // note: we set the csrf in a cookie, but look for it in request headers
    csrfCookie := http.Cookie{
        Name:     "csrf",
        Value:    csrf,
        Expires:  userSession.ExpiresAt,
        Path:     "/",
        HttpOnly: false,
        Secure:   false, // note: can't use secure cookies in development
    }
    http.SetCookie(w, &csrfCookie)

    w.WriteHeader(http.StatusOK)
})

var requiresSession = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    userSession, err := sesh.GetUserSession(r)
    if err != nil {
        log.Printf("Err fetching user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    // nil session pointers indicate a 401 unauthorized
    if userSession == nil {
        http.Error(w, "Unathorized", http.StatusUnauthorized)
        return
    }
    log.Printf("In require; user session expiration before extension: %v\n", userSession.ExpiresAt.UTC())

    myJSON := SessionJSON{}
    if err := json.Unmarshal([]byte(userSession.JSON), &myJSON); err != nil {
        log.Printf("Err unmarshalling json: %v\n", err)
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    log.Printf("In require; user's custom json: %v\n", myJSON)

    // note: we set the csrf in a cookie, but look for it in request headers
    csrf := r.Header.Get("X-CSRF-Token")
    if csrf != myJSON.CSRF {
        log.Printf("Unauthorized! CSRF token doesn't match user session")
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    // note that session expiry's need to be manually extended
    if err = sesh.ExtendUserSession(userSession, r, w); err != nil {
        log.Printf("Err extending user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    log.Printf("In require; users session expiration after extension: %v\n", userSession.ExpiresAt.UTC())

    // need to extend the csrf cookie, too
    csrfCookie := http.Cookie{
        Name:     "csrf",
        Value:    csrf,
        Expires:  userSession.ExpiresAt,
        Path:     "/",
        HttpOnly: false,
        Secure:   false, // note: can't use secure cookies in development
    }
    http.SetCookie(w, &csrfCookie)

    w.WriteHeader(http.StatusOK)
})

var clearSession = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    userSession, err := sesh.GetUserSession(r)
    if err != nil {
        log.Printf("Err fetching user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    // nil session pointers indicate a 401 unauthorized
    if userSession == nil {
        http.Error(w, "Unathorized", http.StatusUnauthorized)
        return
    }

    log.Printf("In clear; session: %v\n", userSession)

    myJSON := SessionJSON{}
    if err := json.Unmarshal([]byte(userSession.JSON), &myJSON); err != nil {
        log.Printf("Err unmarshalling json: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    log.Printf("In require; user's custom json: %v\n", myJSON)

    // note: we set the csrf in a cookie, but look for it in request headers
    csrf := r.Header.Get("X-CSRF-Token")
    if csrf != myJSON.CSRF {
        log.Printf("Unauthorized! CSRF token doesn't match user session")
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    if err = sesh.ClearUserSession(userSession, w); err != nil {
        log.Printf("Err clearing user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }

    // need to clear the csrf cookie, too
    aLongTimeAgo := time.Now().Add(-1000 * time.Hour)
    csrfCookie := http.Cookie{
        Name:     "csrf",
        Value:    "",
        Expires:  aLongTimeAgo,
        Path:     "/",
        HttpOnly: false,
        Secure:   false, // note: can't use secure cookies in development
    }
    http.SetCookie(w, &csrfCookie)

    w.WriteHeader(http.StatusOK)
})

func main() {
    seshStore := store.New(store.Options{})

    // e.g. `$ openssl rand -base64 64`
    seshAuth, err := auth.New(auth.Options{
        Key: []byte("DOZDgBdMhGLImnk0BGYgOUI+h1n7U+OdxcZPctMbeFCsuAom2aFU4JPV4Qj11hbcb5yaM4WDuNP/3B7b+BnFhw=="),
    })
    if err != nil {
        log.Fatal(err)
    }

    seshTransport := transport.New(transport.Options{
        HTTPOnly: true,
        Secure:   false, // note: can't use secure cookies in development!
    })

    sesh = sessions.New(seshStore, seshAuth, seshTransport, sessions.Options{})

    http.HandleFunc("/issue", issueSession)
    http.HandleFunc("/require", requiresSession)
    http.HandleFunc("/clear", clearSession) // also requires a valid session

    log.Println("Listening on localhost:3000")
    log.Fatal(http.ListenAndServe("127.0.0.1:3000", nil))
}

// thanks
// https://astaxie.gitbooks.io/build-web-application-with-golang/en/06.2.html#unique-session-ids
func generateKey() (string, error) {
    b := make([]byte, 16)
    if _, err := io.ReadFull(rand.Reader, b); err != nil {
        return "", err
    }
    return base64.URLEncoding.EncodeToString(b), nil
}

Author: Adam-hanna
Source Code: https://github.com/adam-hanna/sessions 
License: MIT License

#go #golang 

What is GEEK

Buddha Community

Dead Simple, Highly Performant, Highly Customizable Sessions Service

Make Your Business Popular On the Internet with search engine optimization services India

As a small business owner, you should never think that SEO services are not for you. The search engine optimization services India from this digital marketing agency offer SEO services for small businesses and enterprises to make sure that they get in competition with bigger websites. They deliver on-page, off-page, local SEO and ecommerce SEO services.

#search engine optimization services india #seo services india #affordable seo services india #seo services provider #website seo services #outsource seo services india

Waylon  Bruen

Waylon Bruen

1646844480

Dead Simple, Highly Performant, Highly Customizable Sessions Service

If you're interested in jwt's, see my jwt library!

Sessions

A dead simple, highly performant, highly customizable sessions service for go http servers.

By default, the service stores sessions in redis, and transports sessions to clients in cookies. However, these are easily customizeable. For instance, the storage interface only implements three methods:

// ServiceInterface defines the behavior of the session store
type ServiceInterface interface {
    SaveUserSession(userSession *user.Session) error
    DeleteUserSession(sessionID string) error
    FetchValidUserSession(sessionID string) (*user.Session, error)
}

README Contents:

  1. Quickstart
  2. Performance
  3. Design
  4. API
  5. Test Coverage
  6. Example
  7. License

Quickstart

var sesh *sessions.Service

// issue a new session and write the session to the ResponseWriter
userSession, err := sesh.IssueUserSession("fakeUserID", "{\"foo\":\"bar\"}", w)
if err != nil {
    log.Printf("Err issuing user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}

...

// Fetch a pointer to a valid user session from a request. A nil pointer indicates no or invalid session
userSession, err := sesh.GetUserSession(r)
if err != nil {
    log.Printf("Err fetching user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}
// nil session pointers indicate a 401 unauthorized
if userSession == nil {
    http.Error(w, "Unathorized", http.StatusUnauthorized)
    return
}

...

// Extend session expiry. Note that session expiry's need to be manually extended
if err := sesh.ExtendUserSession(userSession, r, w); err != nil {
    log.Printf("Err extending user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}

...

// Invalidate a user session, deleting it from redis and expiring the cookie on the ResponseWriter
if err := sesh.ClearUserSession(userSession, w); err != nil {
    log.Printf("Err clearing user session: %v\n", err)
    http.Error(w, "Internal Server Error", http.StatusInternalServerError)
    return
}

Performance

Benchmarks require a redis-server running. Set the REDIS_URL environment variable, otherwise the benchmarks look for ":6379".

YMMV

$ (cd benchmark && go test -bench=.)

setting up benchmark tests
BenchmarkBaseServer-2              20000             72479 ns/op
BenchmarkValidSession-2            10000            151650 ns/op
PASS
shutting down benchmark tests
ok      github.com/adam-hanna/sessions/benchmark        3.727s

Design

By default, the service stores sessions in redis, and transports hashed sessionIDs to clients in cookies. However, these are easily customizeable through the creation of custom structs that implement the interface.

The general flow of the session service is as follows:

  1. Create store, auth and transport services by calling their respective New(...) functions (or create your own custom services that implement the service's interface methods). Then pass these services to the sessions.New(...) constructor.
  2. After a user logs in, call the sessions.IssueUserSession(...) function. This function first creates a new user.Session. SessionIDs are RFC 4122 version 4 uuids. Next, the service hashes the sessionID with the provided key. The hashing algorithm is SHA-512, and therefore the key used should be between 64 and 128 bytes. Then, the service stores the session in redis and finally writes the hashed sessionID to the response writer in a cookie. Sessions written to the redis db utilize EXPIREAT to automatically destory expired sessions.
  3. To check if a valid session was included in a request, use the sessions.GetUserSession(...) function. This function grabs the hashed sessionID from the session cookie, verifies the HMAC signature and finally looks up the session in the redis db. If the session is expired, or fails HMAC signature verification, this function will return a nil pointer to a user session. If the session is valid, and you'd like to extend the session's expiry, you can then call session.ExtendUserSession(...). Session expiry's are never automatically extended, only through calling this function will the session's expiry be extended.
  4. When a user logs out, call the sessions.ClearUserSession(...) function. This function destroys the session in the db and also destroys the cookie on the ResponseWriter.

API

user.Session

type Session struct {
    ID        string
    UserID    string
    ExpiresAt time.Time
    JSON      string
}

Session is the struct that is used to store session data. The JSON field allows you to set any custom information you'd like. See the example

IssueUserSession

func (s *Service) IssueUserSession(userID string, json string, w http.ResponseWriter) (*user.Session, error)

IssueUserSession grants a new user session, writes that session info to the store and writes the session on the http.ResponseWriter.

This method should be called when a user logs in, for example.

ClearUserSession

func (s *Service) ClearUserSession(userSession *user.Session, w http.ResponseWriter) error

ClearUserSession is used to remove the user session from the store and clear the cookies on the ResponseWriter.

This method should be called when a user logs out, for example.

GetUserSession

func (s *Service) GetUserSession(r *http.Request) (*user.Session, error)

GetUserSession returns a user session from the hashed sessionID included in the request. This method only returns valid sessions. Therefore, sessions that have expired or that fail signature verification will return a nil pointer.

ExtendUserSession

func (s *Service) ExtendUserSession(userSession *user.Session, r *http.Request, w http.ResponseWriter) error

ExtendUserSession extends the ExpiresAt of a session by the Options.ExpirationDuration

Note that this function must be called, manually! Extension of user session expiry's does not happen automatically!

Testing Coverage

ok      github.com/adam-hanna/sessions            9.012s  coverage: 94.1% of statements
ok      github.com/adam-hanna/sessions/auth        0.003s  coverage: 100.0% of statements
ok      github.com/adam-hanna/sessions/store        0.006s  coverage: 85.4% of statements
ok      github.com/adam-hanna/sessions/benchmark    0.004s  coverage: 0.0% of statements [no tests to run]
ok      github.com/adam-hanna/sessions/transport    0.004s  coverage: 95.2% of statements
ok      github.com/adam-hanna/sessions/user        0.003s  coverage: 100.0% of statements

Tests are broken down into three categories: unit, integration and e2e. Integration and e2e tests require a connection to a redis server. The connection address can be set in the REDIS_URL environment variable. The default is ":6379".

To run all tests, simply:

$ go test -tags="unit integration e2e" ./...

// or
$ make test

// or
$ make test-cover-html && go tool cover -html=coverage-all.out

To run only tests from one of the categories:

$ go test -tags="integration" ./...

To run only unit and integration tests:

$ go test -tags="unit integration" ./...

Example

The following example is a demonstration of using the session service along with a CSRF code to check for authentication. The CSRF code is stored in the user.Session JSON field.

package main

import (
    "crypto/rand"
    "encoding/base64"
    "encoding/json"
    "io"
    "log"
    "net/http"
    "time"

    "github.com/adam-hanna/sessions"
    "github.com/adam-hanna/sessions/auth"
    "github.com/adam-hanna/sessions/store"
    "github.com/adam-hanna/sessions/transport"
)

// SessionJSON is used for marshalling and unmarshalling custom session json information.
// We're using it as an opportunity to tie csrf strings to sessions to prevent csrf attacks
type SessionJSON struct {
    CSRF string `json:"csrf"`
}

var sesh *sessions.Service

var issueSession = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    csrf, err := generateKey()
    if err != nil {
        log.Printf("Err generating csrf: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }

    myJSON := SessionJSON{
        CSRF: csrf,
    }
    JSONBytes, err := json.Marshal(myJSON)
    if err != nil {
        log.Printf("Err marhsalling json: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }

    userSession, err := sesh.IssueUserSession("fakeUserID", string(JSONBytes[:]), w)
    if err != nil {
        log.Printf("Err issuing user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    log.Printf("In issue; user's session: %v\n", userSession)

    // note: we set the csrf in a cookie, but look for it in request headers
    csrfCookie := http.Cookie{
        Name:     "csrf",
        Value:    csrf,
        Expires:  userSession.ExpiresAt,
        Path:     "/",
        HttpOnly: false,
        Secure:   false, // note: can't use secure cookies in development
    }
    http.SetCookie(w, &csrfCookie)

    w.WriteHeader(http.StatusOK)
})

var requiresSession = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    userSession, err := sesh.GetUserSession(r)
    if err != nil {
        log.Printf("Err fetching user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    // nil session pointers indicate a 401 unauthorized
    if userSession == nil {
        http.Error(w, "Unathorized", http.StatusUnauthorized)
        return
    }
    log.Printf("In require; user session expiration before extension: %v\n", userSession.ExpiresAt.UTC())

    myJSON := SessionJSON{}
    if err := json.Unmarshal([]byte(userSession.JSON), &myJSON); err != nil {
        log.Printf("Err unmarshalling json: %v\n", err)
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    log.Printf("In require; user's custom json: %v\n", myJSON)

    // note: we set the csrf in a cookie, but look for it in request headers
    csrf := r.Header.Get("X-CSRF-Token")
    if csrf != myJSON.CSRF {
        log.Printf("Unauthorized! CSRF token doesn't match user session")
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    // note that session expiry's need to be manually extended
    if err = sesh.ExtendUserSession(userSession, r, w); err != nil {
        log.Printf("Err extending user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    log.Printf("In require; users session expiration after extension: %v\n", userSession.ExpiresAt.UTC())

    // need to extend the csrf cookie, too
    csrfCookie := http.Cookie{
        Name:     "csrf",
        Value:    csrf,
        Expires:  userSession.ExpiresAt,
        Path:     "/",
        HttpOnly: false,
        Secure:   false, // note: can't use secure cookies in development
    }
    http.SetCookie(w, &csrfCookie)

    w.WriteHeader(http.StatusOK)
})

var clearSession = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    userSession, err := sesh.GetUserSession(r)
    if err != nil {
        log.Printf("Err fetching user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    // nil session pointers indicate a 401 unauthorized
    if userSession == nil {
        http.Error(w, "Unathorized", http.StatusUnauthorized)
        return
    }

    log.Printf("In clear; session: %v\n", userSession)

    myJSON := SessionJSON{}
    if err := json.Unmarshal([]byte(userSession.JSON), &myJSON); err != nil {
        log.Printf("Err unmarshalling json: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    log.Printf("In require; user's custom json: %v\n", myJSON)

    // note: we set the csrf in a cookie, but look for it in request headers
    csrf := r.Header.Get("X-CSRF-Token")
    if csrf != myJSON.CSRF {
        log.Printf("Unauthorized! CSRF token doesn't match user session")
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    if err = sesh.ClearUserSession(userSession, w); err != nil {
        log.Printf("Err clearing user session: %v\n", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }

    // need to clear the csrf cookie, too
    aLongTimeAgo := time.Now().Add(-1000 * time.Hour)
    csrfCookie := http.Cookie{
        Name:     "csrf",
        Value:    "",
        Expires:  aLongTimeAgo,
        Path:     "/",
        HttpOnly: false,
        Secure:   false, // note: can't use secure cookies in development
    }
    http.SetCookie(w, &csrfCookie)

    w.WriteHeader(http.StatusOK)
})

func main() {
    seshStore := store.New(store.Options{})

    // e.g. `$ openssl rand -base64 64`
    seshAuth, err := auth.New(auth.Options{
        Key: []byte("DOZDgBdMhGLImnk0BGYgOUI+h1n7U+OdxcZPctMbeFCsuAom2aFU4JPV4Qj11hbcb5yaM4WDuNP/3B7b+BnFhw=="),
    })
    if err != nil {
        log.Fatal(err)
    }

    seshTransport := transport.New(transport.Options{
        HTTPOnly: true,
        Secure:   false, // note: can't use secure cookies in development!
    })

    sesh = sessions.New(seshStore, seshAuth, seshTransport, sessions.Options{})

    http.HandleFunc("/issue", issueSession)
    http.HandleFunc("/require", requiresSession)
    http.HandleFunc("/clear", clearSession) // also requires a valid session

    log.Println("Listening on localhost:3000")
    log.Fatal(http.ListenAndServe("127.0.0.1:3000", nil))
}

// thanks
// https://astaxie.gitbooks.io/build-web-application-with-golang/en/06.2.html#unique-session-ids
func generateKey() (string, error) {
    b := make([]byte, 16)
    if _, err := io.ReadFull(rand.Reader, b); err != nil {
        return "", err
    }
    return base64.URLEncoding.EncodeToString(b), nil
}

Author: Adam-hanna
Source Code: https://github.com/adam-hanna/sessions 
License: MIT License

#go #golang 

Top-Notch 3d Design Services | 3d Printing Prototype Service

3D Design Service Provider

With the advancement in technology, many products have found a dire need to showcase their product virtually and to make the virtual experience as clear as actual a technology called 3D is used. The 3D technology allows a business to showcase their products in 3 dimensions virtually.

Want to develop an app that showcases anything in 3D?

WebClues Infotech with its expertise in mobile app development can seamlessly connect a technology that has the capability to change an industry with its integration in the mobile app. After successfully serving more than 950 projects WebClues Infotech is prepared with its highly skilled development team to serve you.

Want to know more about our 3D design app development?

Visit us at
https://www.webcluesinfotech.com/3d-design-services/

Visit: https://www.webcluesinfotech.com/3d-design-services/

Share your requirements https://www.webcluesinfotech.com/contact-us/

View Portfolio https://www.webcluesinfotech.com/portfolio/

#3d design service provide #3d design services #3d modeling design services #professional 3d design services #industrial & 3d product design services #3d web design & development company

Evelyn  Lucy

Evelyn Lucy

1619096139

Multi Service App Development | On-Demand Multi-Services App Solution

Having a multi-services app would help you to excel in your on-demand services business. An on-demand app solution empowers entrepreneurs to offer multiple on-demand services in a single app. In short, this is an efficient app to run a business successfully. If you are an entrepreneur who plans to start a business with the multi-services app, go forward with the multi-service app development.

What are the multiple services offered in the on-demand multi-services app?

Services are categorized as follows.
Ride services – Taxi ride, Moto ride, Car rental, and Moto rental.
Delivery services – Food delivery, Courier delivery, Logistics delivery, Grocery delivery, Medicine delivery, Flower delivery, Fuel delivery, and Plant delivery.
Other services – Plumber, Electrician, Car wash, Fitness, Handyman, Car repair, and beauty services.

Apart from these, you can consider integrating several other services while developing your app.

3 Significant reasons to invest in the on-demand multi-services app

The first and foremost reason why customers use this app is the on-demand multi-service on one platform. Usually, people do not like to install so many apps for availing various services. Instead, they can have a single app for that. This is the reason why the demand for such apps is high.

Next, the incurred cost is less in this app when compared to the single service app. With the seamless navigation feature, customers can easily avail of any services with just a few taps.

Thirdly, they feel more convenient in availing themselves various services in one platform.

Future scope of the multi-service industry

There are 7.6 million users for the multi-service apps in 2019. Recently, the demand for such apps is high considerably due to the covid-19 pandemic. It is expected to flourish more in the future. By 2023, this industry will hit 161.74 billion. This is so inspiring and so many entrepreneurs plan to propel into this industry in 2021.

Consider the following aspects for multi-service app development

Integrate the Multilingual and Multiple currencies features

Never let language be a barrier to your business. Therefore, incorporate the multilingual feature so that customers can use the app in their languages.

The global launch will help you to have a more extensive user base for your app. Just like language, do not let the currency restrict you from launching your app across many countries.

User-friendly design

The UI/UX of the app has to be simple and appealing. This plays a vital role in gaining more customers. If the design is not user-friendly and unimpressive, they won’t prefer your app for the next time. Instead, they prefer using your competitors’ app for availing multiple services. To gain new customers and to retain the existing customers, focus on the app design.

App platform

To cover all the audiences, consider launching the app on both the Android and iOS platforms. Decide on which platform you will launch the app based on your target audience.

White-label solution

It is a known fact that making an app from scratch needs more time and requires a considerable amount of money. On the counter side, creating the app using the white-label solution is budget-friendly and time-conserving. Because, it is a readily available solution. Upon making modifications, you can launch the app instantly. Being the customizable solution, any new features can be incorporated based on the requirements.

Wrap up,

The decision of starting a business with the on-demand multi-services app is good as the market will flourish further in the upcoming days. Take away the points from this blog to withstand in the highly competitive market. Reach out to Uberlikeapp for multi-services app development. We provide a customizable app solution that is scalable based on your needs.

#on demand multi services app #multi services app development #multi service booking app #on-demand service app clone #on-demand multi-services app solution

Akshaya 7

1608125898

Guide On How The Professional French Translation Services Work In India

French translation services are very much adequate for the people who want to convey their brand reports, information, and other essentials to the French clients or audience. If the businesses can convey with the clients in their native language, then probably the business will experience a progressive hike.

French is one of the best known European languages, and more than 80 million people across the globe speak this language. Therefore, if you are running an MNC, then you might need to leverage the opportunity for seeking sales or business from those 80 million people across the globe.
For better clarity, you must know that French is the second language for more than 100 million people across the globe. If you are wondering why you need the French translation services, then follow this article till the end to get the proper guide on how the professional French translation services can help you out.
At NNB Transtech, we provide All Foreign Language Translation Services.

Hiring Professional French Translation Services!

The professional French translation services offered by the major companies are to help the new and established companies reach out to the French-speaking audience or clients for better business. There are professional firms that are dedicated to offering the best translation services to the top MNCs with the help of subject matter translation experts.

The best part about hiring professionals is that they have a team of French experts who are proficient in different backgrounds. For instance, the professional French translators working in a firm are knowledgeable about Finance, healthcare, automobile, business, and other such niches. Therefore, accuracy is its prime commitment. So, the first step to avail the perfect French translation is to hire the best firm that is offering you ideal French translation services as per your needs and requirements within the specified budget.

The top corporations & organizations are very commonly in touch with clients and consumers all across the globe. Therefore, getting the documents and essentials translated into French is important for the companies to establish healthy communication with the French-speaking audience or clients.

Explain Your French Translation Project to the Professionals

The French translation service providers will assign a project manager for your translation needs. They are trained to understand and pen down everything you need and want to be mentioned in the translated document or promotional media.

Whether you seek French translation for websites or documents, you need to elaborate on the business or field it is related to. Once you do that, you will be asked for giving details on the facts and figures that you don’t want to be altered and should be present in the final output compulsorily.
The medical documents and accounting papers need to be perfectly accurate in terms of figures and facts. No business or individual will want it altered! The skilled employees are well aware of the facts and figures as they are subject matter experts.
But even then, they prefer taking instructions from the clients to overlook the work done before delivering it to the client.

After you explain your project, you will be given a deadline, and the work will begin. Irrespective of the complexity of the document, the professional translators will get it done without much hassle. The professional firms have the right certifications to handle your diverse translation projects.
Start of the Translation Project

After you have handed over your important document or other essential for the French translation, the experts will commence with the work. They have the right tools and expertise to start with quality translation work.

The professional French translation firms make sure to do the translation humanly. It is so because the machines are not accurate and it takes a lot of time to review the machine-translated documents. Therefore, to promote perfection in terms of the French translation, the professionals get it done by humans for utmost accuracy on priority.

Whether you have a medical, technical, financial, or legal document for translation, the experts are certified professionals in the respective background to handle your project. They will start as per your instructions that you stated in the previous step.

You can get the translated document in the desired format such as Adobe in Design, PowerPoint, MSWord, PDF, and others. If you are willing to get your documents translated from English to French without compromising on quality and at affordable pricing, seeking help from professionals is the best option for you.

Delivery of the Translated French Document

After the document has been translated, it is then sent to the review team to look at the accuracy of all the facts and figures. It is the job of the professionals to look after the accuracy of the documents to make them convey the right information to the French clients and consumers.
They are determined to offer you excellent quality, and for that, a proper review is highly demanded before the delivery of the work to the clients. Along with that, the professional firms are open for any type of edits as per the request from the clients.

The professionals leave no loopholes for bringing up the chances of edits. But even if you need more perfection, you can always reach out to your outsourced team to get the edits done without any additional charges. Therefore, the professionals are highly preferred for French translation overdoing it on your own.

Conclusion

These are the steps that act as the guide for you as well as the professional French translation firms to help you offer the best services at affordable pricing. You might be wondering whether to take the help of professional firms or hire an individual language expert.
An amateur language expert might not be proficient with tools and essentials to handle your multiple needs in the French translation project. You might be limited on the outputs by hiring individuals for your projects.

Therefore, it is better to seek help from professionals to help your business get the best benefits by putting up the best French translated documents for healthy communication with French-speaking clients or consumers across the globe.

#french translation services #spanish translation services #japanese translation services #german translation services #translation services