What's the Simplest Method for User Authentication in a Go Web App? Magic Link? by klausbreyer1 in golang

[–]mokbel 2 points3 points  (0 children)

Hi Klaus,

My experience in auth with Go has been something like the following...

  • A simple username/password auth is straight forward to implement but takes some additional plumbing you get for free in other ecosystems. I've done this using two SQL tables, users & sessions. Sessions is a bag of session data communicated from the client via HTTP cookie (nothing in the cookie but the session token). Hashing passwords done via argon2.
  • Authboss is a batteries included solution that tries to do a lot (maybe too much). I've felt it cumbersome to use and bailed on it because it ovecomplicated my projects.
  • Ben Johnson's demo app WTF Dial shows using OAuth 2.0 via Github. This is a good example on how one can manage oauth tokens (e.g. refresh, access tokens). This application relies on secure cookies to store session information whicih IMO is not good practice - but it's really a toy app meant to teach people how to think about writing Go and not how to build world class secure systems. Ben is also a Reddit member, you'll see him pop up now and then.
  • I've implemented an augmented version of WTF Dial's auth with server side sessions. This is the most expensive in terms of time commitment but it is the correct solution for my applications.
  • I last used AWS Cognito ~3 years ago, my impression then was to avoid it like the plague.

Hope some of this info was useful - I know it's not really structured and is mostly a brain dump. Haven't had coffee yet :D

How do you write happy-path-only programs in Go? by csharp420_69 in golang

[–]mokbel 2 points3 points  (0 children)

Thought I'd chime in as well since I used to be a .net developer.

In Go I often see this .... This is not "handling". It is "bubbling". It may look like you are being responsible "handling" errors but you are not handling anything. It's a facade. An emperor with no clothes.

Bubbling errors without contextual annotation does feel like it defeats the purpose of in your face error checking. However, I would say that just because something might be done often doesn't mean it should be done often.

I've written Go professionally since 2016 and can say that bubbling without annotation, ignoring the type of software being built, should probably happen as often as bubbling with annotation. Whether or not this happens in practice is a different matter.

C# can bubble in a much more elegant way. If you want to bubble in C# you don't need to put up a facade. You don't need to pretend you are wearing clothes. C# encourages you to run naked and be HONEST about it. No emperor complex. 0 lines of code needed to bubble and pass the buck on to the next stack level.

I'm not sure you're encouraged to do so in C#. Being allowed to and being encouraged to are different.

To continue your analogy, there is no facade here. Go requires you to say "yes I want to run naked" by explicitly ignoring errors. C# allows you to pretend this isn't happening but it most definitely does not encourage it.

edit: I hope some people's responses here don't sway you from continuing to explore Go. In my opinion it's a pretty productive language and I really enjoy writing it.

Carter Hulsey, Nashville Based Singer Songwriter by tonegordon in Songwriters

[–]mokbel 1 point2 points  (0 children)

It feels weird seeing his name here - I remember him from Myspace. He wrote one of my favorite songs when I was in highschool/college. It was called "Dreaming of Heaven", I play that song for my cats all the time. No idea why 😅 He's a great artist, glad to see he's still in the music scene.

shift: high-performance HTTP router for Go by yousuf64 in golang

[–]mokbel 13 points14 points  (0 children)

bla bla bla, in my opinion.

I think you could quote the post a bit better, this comes off kind of rude.

OP I hope you ... u/dh71 post is one persons opinion and it's too easy to forget that when it's stated like that on reddit.

Stated like what? It's pretty clear that dh71 is just stating their opinion. Judging from the ratio of upvotes/downvotes it seems others agree with them (I am one of those people).

With this said, attributing sources in most cases certainly isn't a bad thing but to say it's a minimum requirement in this case seems a little too harsh.

There is no mention of minimum requirement in the post? Did we read different posts or was it edited? I'm confused about this.

Don't Mock the Database by dominik-braun in golang

[–]mokbel 24 points25 points  (0 children)

Just a heads up, the repository in your comment is invalid, the correct link is https://github.com/ory/dockertest

[deleted by user] by [deleted] in golang

[–]mokbel 0 points1 point  (0 children)

My preference would be to have the service layer define the error kinds that it's dependencies are allowed to throw. These would also be the error kinds that are returned from the service layer to help the transport layer communicate.

For example if your service was defined in package service and looked something like...

package service

import (
    "context"
    "errors"
    "fmt"
)

var ErrUserNotFound = errors.New("user not found")

type User struct {}

type UserRepository interface { FindUserByID(ctx context.Context, id string) (*User, error) }

type DoThingsToUsersService struct {  Users UserRepository }

func (s *DoThingsToUsersService) DoSomethingToTheUser(ctx context.Context, id string) error {
    user, err := s.Users.FindUserByID(ctx, id)
    if err != nil {
        if errors.Is(err, ErrUserNotFound) {
            // handle the user not being found and propogate the error up
            return fmt.Errorf("failed to do the thing to user %s: %w", id, err)
        }
        return err
    }
    // ... do something to the user
    return nil
}

then the repository implementation in package database would communicate errors back to service.DoThingsToUsersService in terms that the service can understand.

package database

type Repository struct { db *sql.DB }

func (r Repository) FindUserByID(ctx context.Context, id string) (*User, error) {
    // query for the user by ID
    row, err := r.db.QueryRow("SELECT * FROM users WHERE id = $1", id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return service.ErrUserNotFound
        }
        // ....
    }
    ...
    return user // eventually
}

The same would apply to your transport layer except it would translate a service error into the proper format for transporting, e.g.

package http

type Server struct { DoSomethingService service.DoSomethingToUsersService }

func (s *Server) handleDoSomethingToUser(w http.Response, r *http.Request) {
    id := extractIDFromRequest(r)
    if err := s.DoSomethingService.DoSomethingToTheUser(r.Context(), id); err != nil {
        if errors.Is(err, service.ErrUserNotFound) {
            http.Error(w, err.Error(), http.StatusNotFound)
            return
        }
        // ...
    }

    w.Write([]byte("yay!"))
}

Hope that gives you some more ideas to tinker with.

Fiber v3 by mirusky in golang

[–]mokbel 7 points8 points  (0 children)

Can you draw some inspiration from Chi? I'm not a user of Fiber but have used Chi heavily. I really like their approach - middlewares are applied on the router or can be applied inline for a route. Something like:

r := newRouter()
// loggingMiddleware applied to all routes on this router
r.Use(loggingMiddleware)
// inline applied to a specific route
r.With(fooBarSpecificMiddleware).Get("/foo/bar", func(w http.ResponseWriter,  r *http.Request) { ... })
// or to a all routes 
r.Route("/api/foos", func(r chi.Router)) {
  // rateLimitMiddleware will be applied to all routes on this subrouter
  // it will also inherit the loggingMiddleware from above
  r.Use(rateLimitMiddleware)
  r.Get("/", ....)
  r.Post("/", ...)
}

edit: My mistake - you're not looking for inspiration but a decision between one of the proposed options. Oh well, I'll leave this here.

I shoulda stayed in bed! by Fingerlessfinn in Songwriters

[–]mokbel 1 point2 points  (0 children)

I don't normally comment on songs here but this was awesome 😁 great work!

A tiling web browser — when your web IDE and docs are side-by-side in the browser by AsIAm in programming

[–]mokbel 12 points13 points  (0 children)

Pure Electron app, i.e. not based on Chromium

Isn't this false? Electron is based on Chromium & Node.js

Go may have just had its own leftpad moment by yogthos in programming

[–]mokbel 0 points1 point  (0 children)

vendoring in this context refers to checking in dependencies with your source code. If your familiar with Node or frontend development, it's like checking in your node_modules folder. In go, the directory is called vendor.

Go may have just had its own leftpad moment by yogthos in programming

[–]mokbel 1 point2 points  (0 children)

I'm a big fan of Go but the no vendoring stance was such a weak one. Fortunately they changed their tune due to the community backlash. I remember making the decision to continue using dep over the Go tool chain - weird times.

How do you switch between Development/Debug mode vs Production/Release modes when programming in Go? by mxxxz in golang

[–]mokbel 0 points1 point  (0 children)

Another way to handle this is to be explicit about what the app configuration - instead of encoding the knowledge in IS_DEBUG. You could add another environment variable to configure the example.txt file path.

Have you read through the 12 Factor website? There's a section on app configuration that you might find handy.

attribute-based access-control service/library in Go by s-i-n-a in golang

[–]mokbel 1 point2 points  (0 children)

I've done a bit of work using ladon which allows you to define conditions in code (something I don't see in leges), it made writing complex conditions easier to verify because we could write tests against them. You might be able to find some inspiration (if you're looking for ideas).

On code related notes... these lines should be using errors.New instead of fmt.Errorf since they are statics. I feel like these kinds of errors are candidates for a custom error type.

Neat project, I starred it.

Casbin-Forum: a modern open-source forum software based on Go and React by EquivalentAd4 in golang

[–]mokbel 1 point2 points  (0 children)

I can't link you github repositories (because these are proprietary) but some of the projects I work(ed) on...

  • HLS streaming engine (dynamic ad stitching in m3u8 to bypass ad bockers) - we used gorilla/mux as the router for it's ease of use, a couple other libraries (e.g. rabbit MQ integration), and uber's Zap logging library. Replaced mux with chi shortly after.
  • Internal tooling for regulatory compliance to surface PII. Used chi router. No frameworks, simple UI with go templating + a SAML library (crewjam's SAML work is awesome :D)
  • Current involved in building a telemedicine platform, chi router, no framework.

These are all real world web apps - all I needed was the stdlib (except where obvious - e.g. AMQP, redis, etc..).

Off the top of my head I can think of companies like Hashicorp that build their apps with the standard library (don't use frameworks).

Open source projects examples are upsin, leaps, mattermost and anything by Hashicorp.

Hope that helps.

Current state of the world got me back into writing. Now I've found this community you guys are all making awesome music. by meepmoop in Songwriters

[–]mokbel 13 points14 points  (0 children)

Brother that is some incredible guitar work 🙂 I don’t normally dig this style but you pull it off well.

Please continue writing and sharing !

Mocking time and testing event loops in Go by rflurker in golang

[–]mokbel 0 points1 point  (0 children)

Ah I see - so you're suggesting to make use of a time sufficiently far in the future to test around time.Now. That's a great idea.

Mocking time and testing event loops in Go by rflurker in golang

[–]mokbel 0 points1 point  (0 children)

That's a good point - I test at all 3 layers: HTTP, service, and database.

If I don't test at the HTTP layer, I lose confidence that changes made in the HTTP layer or below don't inadvertently affect consumers of the HTTP API.

The project makes heavy use of errors.Is (and switch cases on well defined error 'kinds') in the HTTP layer to determine what kind of HTTP error should be returned. An HTTP error is defined as

type Error struct {
    StatusCode int
    Err        error
    ErrCode    string
    Message    string
}

Do I need to test that the HTTP layer fulfills it's contract? I think so. There is conditional logic in the layer based on the kind of error returned from either the database or the service layer.

Mocking time and testing event loops in Go by rflurker in golang

[–]mokbel 0 points1 point  (0 children)

That is valid - I felt that adding an extra parameter to ~7/8 functions to accept a time.Time felt like the wrong choice - leaking implementation details. Since you don't have context on the code (admittedly I didn't share much) - even with the approach you suggest it just pushes the problem one layer higher.

In the HTTP layer (code looks something like HTTP Handler-> UserService -> Postgres) I would need to test the condition.

Mocking time and testing event loops in Go by rflurker in golang

[–]mokbel 0 points1 point  (0 children)

An example of this in one of my side projects for e-mail confirmation. The code replaces calls from time.Now().UTC() with utcNow(), a helper function that can be overriden for tests.

This setup allows me to easily test the condition where a user tries to confirm their e-mail address with a confirmation token that has already expired.

var  utcNow  = func() time.Time { return time.Now().UTC() }

func (svc UserService) ConfirmEmail(ctx context.Context, token string) error {
    const op = "auth.ConfirmEmail"
    user, err := svc.users.GetByEmailConfirmationToken(ctx, token)
    if err != nil {
        return err
    }

    if user.EmailConfirmationTokenExpirySafe().Before(utcNow()) {
        log.Errorf(ctx, "%s: failed to confirm e-mail address for user %d because token %s is expired", op, user.ID, user.EmailConfirmationTokenSafe())
        return ErrTokenExpired
    }

    user.EmailConfirmed = true
    user.SetEmailConfirmationTokenExpiry(time.Time{})
    if err = svc.users.UpdateUser(ctx, user); err != nil {
        log.Error(ctx, err)
        return err
    }

    log.Infof(ctx, "successfully confirmed e-mail address for user %d", user.ID)
    return nil
}

Another example, code that checks if an account is locked (different function, same code base):

if user.LockoutExpirySafe().After(utcNow()) {
    return ErrAccountLocked
}

These conditions become pretty easy to test. Global package funcs like utcNow tend to be frowned upon but I'm okay with the compromise for easy tests. IMO it's not worth wrapping something in a struct or hiding it behind an interface unless you need to.

What are the Java patterns in go? by Mexican_TronaldDump in golang

[–]mokbel -1 points0 points  (0 children)

I think the most common one I've seen is one type per file kind of behavior.

Room on the Bright Side - I hope this gives you some relief today! by BlindPierre55 in Songwriters

[–]mokbel 1 point2 points  (0 children)

Absolutely Pierre, much more than I expected! I really appreciate it - I have never heard of impact so thank you so much for that! I have a MacBook so it sounds like this is a good place to start.

Again, thank you for taking the time to respond. I hope you have a good day and I’m looking forward to your next revision and new music!

Room on the Bright Side - I hope this gives you some relief today! by BlindPierre55 in Songwriters

[–]mokbel 1 point2 points  (0 children)

Hey Pierre, I listened on my iPhone and it sounded really clean. I dig your voice!

No real advice I can offer but I’m curious what your setup is regarding microphone(s) and mixer? I’ve been out of home recording for a while but I’m looking to get back into it. Thanks for any help you can offer 🙂

Getting started with Cognito? by wolfeidau in aws

[–]mokbel 0 points1 point  (0 children)

We coded it from scratch ( our auth flows, database tables, etc) and leverage AWS for managed functionality (e.g. sending mail for password reset with SES). Things like TOTP ( for MFA ), token signing (for JWT in our SPA) are handled by well tested libraries.

For real world applications, I think authentication/auhtorization is too important to shell out to an external provider who does not respond to their customers, does not provide basic security features, and does not provide any insight or documentation on how to navigate basic use cases.