Q:

How to validate user and password on macOS programmatically?

I have an application that asks for the username and password of the user. Currently, for Unix systems (most of them) I read the password hash from /etc/shadow but for macOS is way more cumbersome (they use their own standard) so I wanted to know if there is an easy way to validate usernames and passwords.
I was thinking of using the login program in a weird way for this but it's not the best solution.
I'm using golang but a solution in C or C++ would be useful too.
Edit
The answer from @TheNextman is exactly what I asked for but talking with other people they suggest me to use PAM because it works with multiple OS. It took me some effort to understand it because I was looking for a simple API to verify users and passwords, something like verify(username string, password string) -> boolean but I get that PAM is more general and hence more complex.
But this is the code I ended up using, it's made in GO with C bindings.
package Authentication
import "C"
import (
    "encoding/json"
    "fmt"
    "github.com/vvanpo/golang-pam"
)

func ValidateUser(username, password string) (bool, error) {
    // TODO correct handle of errors
    UserPssword := userPassword{
        Username : username,
        Password : password,
    }
    b, _ := json.Marshal(UserPssword)

    conn := myConHand{
        username:C.CString(string(b)),
    }
    tx, _ := pam.Start("sshd", username, conn)

    r := tx.Authenticate(0)
    if r == pam.SUCCESS{
        return true, nil
    } else {
        return false, nil
    }
}

type userPassword struct {
    Username string
    Password string
}

type myConHand struct {
    username *C.char
}

//C.GoString
func (m myConHand) RespondPAM(msg_style int, msg string) (string, bool) {
    str := C.GoString(m.username)

    var UserPssword userPassword
    _ = json.Unmarshal([]byte(str), &UserPssword)

    switch msg_style {
    case pam.PROMPT_ECHO_OFF:
        fmt.Println("Password!!")
        return UserPssword.Password, true
    case pam.PROMPT_ECHO_ON:
        fmt.Println("Username!!")
        return UserPssword.Username, true
    case pam.ERROR_MSG:
        fmt.Println(msg)
        return "", true
    case pam.TEXT_INFO:
        fmt.Println(msg)
        return "", true
    default:
        return "", true
    }
}

It has some problems, for example I'm using the /etc/pam.d/sshd service because I know that the computers I'll be using have the same service, but I don't know which OS has /etc/pam.d/sshd, the other option will be to create my own service but I'll have to create one for every different standard which I found 3 Linux PAM, openPAM (macos) and Solaris PAM.
But overall I think this is a robust solution because I'm reusing the existing authentication mechanism.

A:

a solution in C or C++ would be useful too

Here is an example in C using CoreServices. Error and result checking are omitted for brevity:
void on_logon(const char* username, const char* password) {
    CSIdentityQueryRef query;
    CFArrayRef idArray = NULL;
    CSIdentityRef result;
    CFStringRef cfUsername = NULL;
    CFStringRef cfPassword = NULL;

    cfUsername = CFStringCreateWithCString(NULL, username, kCFStringEncodingUTF8);
    query = CSIdentityQueryCreateForName(kCFAllocatorDefault, cfUsername, kCSIdentityQueryStringEquals, kCSIdentityClassUser,    CSGetDefaultIdentityAuthority());

    CSIdentityQueryExecute(query, kCSIdentityQueryGenerateUpdateEvents, NULL);

    idArray = CSIdentityQueryCopyResults(query);

    if (CFArrayGetCount(idArray) != 1)
    {
        // Username didn't match...
    }

    result = (CSIdentityRef) CFArrayGetValueAtIndex(idArray, 0);

    cfPassword = CFStringCreateWithCString(NULL, password, kCFStringEncodingUTF8);

    if (CSIdentityAuthenticateUsingPassword(result, cfPassword))
    {
        // Username and password are valid!!
    }

    CFRelease(cfUsername);
    CFRelease(idArray);
    CFRelease(cfPassword);
    CFRelease(query);
}

