When does object creation fail?

I am trying to implement SSO with an open source product, but I keep getting an error that is blocking me. I was wondering if anyone could tell me if it is something in the GO language that I need to accommodate for (I have no experience in the language)
image
I have the above fields passing in the token, and then the object is as below. The object seems to be empty every time I test the SSO functionality. Does every field need to be present for the object to be created correctly?

type User struct {
	ExternalID     string                 `json:"sub"`
	Name           string                 `json:"name"`
	Email          string                 `json:"email"`
	EmailVerified  bool                   `json:"email_verified"`
	UserInfoClaims map[string]interface{} `json:"user_info_claims"`
}

Any advice or explanation is very welcome

It’s hard to tell without knowing exactly what you are doing. Can you post a small example code?

The Go JSON decoder will ignore fields in a JSON object that have no corresponding field in your target struct. It also leaves struct fields initialized to the zero value if they do not exist in the JSON object. From the example data and structcode you posted, the first four fields of your struct ought to be populated.

As @mje said, it’s always better to show what you tried so far.

The open source product I am using is Chirpstack Application Server. I am attempting to get OIDC to work with an Azure provider.
This is the unmarshalling method in oidc.go

The implementation of OIDC login

// OpenIDConnectLogin performs an OpenID Connect login.
func (a *InternalAPI) OpenIDConnectLogin(ctx context.Context, req *pb.OpenIDConnectLoginRequest) (*pb.OpenIDConnectLoginResponse, error) {
	oidcUser, err := oidc.GetUser(ctx, req.Code, req.State)
	if err != nil {
		return nil, helpers.ErrToRPCError(err)
	}

	if !oidcUser.EmailVerified {
		return nil, grpc.Errorf(codes.FailedPrecondition, "email address must be verified before you can login")
	}

	var user storage.User

	// try to get the user by external ID.
	user, err = storage.GetUserByExternalID(ctx, storage.DB(), oidcUser.ExternalID)
	if err != nil {
		if err == storage.ErrDoesNotExist {
			// try to get the user by email and set the external id.
			user, err = storage.GetUserByEmail(ctx, storage.DB(), oidcUser.Email)
			if err != nil {
				// we did not find the user by external_id or email and registration is enabled.
				if err == storage.ErrDoesNotExist && registrationEnabled {
					user, err = a.createAndProvisionUser(ctx, oidcUser)
					if err != nil {
						return nil, helpers.ErrToRPCError(err)
					}
					// fetch user again because the provisioning callback url may have updated the user.
					user, err = storage.GetUser(ctx, storage.DB(), user.ID)
					if err != nil {
						return nil, helpers.ErrToRPCError(err)
					}
				} else {
					return nil, helpers.ErrToRPCError(err)
				}
			}
			user.ExternalID = &oidcUser.ExternalID
		} else {
			return nil, helpers.ErrToRPCError(err)
		}
	}

The getUser method that gives out the error.

func GetUser(ctx context.Context, db sqlx.Queryer, id int64) (User, error) {
	var user User

	err := sqlx.Get(db, &user, `
		select
			*
		from
			"user"
		where
			id = $1
	`, id)
	if err != nil {
		if err == sql.ErrNoRows {
			return user, ErrDoesNotExist
		}
		return user, errors.Wrap(err, "select error")
	}

	return user, nil
}

did you checked if the UnmarshalJSON() method returns an error ?

Hi, thanks for such fast responses.

I am restricted to having to use an enterprise kubernetes environment that has a lot of restrictions I can’t replicate on my local, so I haven’t had the opportunity to step through and debug the code like I normally would.

I would like to ask a GO question again, it might help me get some more understanding of how the code is/isn’t working.
Does GO have something inbuilt that would automatically use this UnmarshalJSON method, or should it be explicitly called somewhere?

If it should be explicitly called somehwere, I may have to open a bug on the git repo

By default json.Unmarshal() does unmarshaling recursively; if one field deinfes the UnmashalJSON() it calls it automatically.

https://pkg.go.dev/encoding/json#Unmarshal
https://pkg.go.dev/encoding/json#Unmarshaler

This example shows how MarshalJSON() works but the concept is the same for Unmarshal

In your case UnmashalJSON() is needed because you need to support bool value as well strings for EmailVerified

1 Like