
/*---------------------------------------------------------------------------*/
/* oauth2_client.h                                                           */
/* copyright (c) innovaphone 2016                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

enum OAuth2ClientTokenError {
    OAUTH2_SUCCESS,
    OAUTH2_ERROR,
    OAUTH2_ERROR_INVALID_SECRET,
    OAUTH2_ERROR_UNAUTHORIZED_CLIENT,
    OAUTH2_ERROR_ACCESS_DENIED,
    OAUTH2_ERROR_POLICY_ENFORCED,
    OAUTH2_ERROR_INVALID_CLIENT,
    OAUTH2_ERROR_INVALID_GRANT,
    OAUTH2_ERROR_INVALID_SCOPE,
    OAUTH2_ERROR_DISABLED_CLIENT,
    OAUTH2_ERROR_INTERNAL_ONLY,
    OAUTH2_ERROR_INVALID_REQUEST,
    OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE,
    OAUTH2_ERROR_INVALID_RESOURCE,
    OAUTH2_ERROR_INTERACTION_REQUIRED,
    OAUTH2_ERROR_TEMPORARILY_UNAVAILABLE,
    OAUTH2_ERROR_CONSENT_REQUIRED,
    OAUTH2_ERROR_PARAMETER_REQUIRED,
    OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE,
    OAUTH2_ERROR_DELETED_CLIENT,
};

class UOAuth2ClientPersist {
public:
    virtual ~UOAuth2ClientPersist() {};

    virtual void OAuth2ClientPersistInitialize(class IOAuth2ClientPersist * const persist, const char * value) = 0;
    virtual void OAuth2ClientPersistInitialized(class IOAuth2ClientPersist * const persist) = 0;
    virtual void OAuth2ClientPersistCloseComplete(IOAuth2ClientPersist const * persist) = 0;
};

class IOAuth2ClientPersist {
public:
    virtual ~IOAuth2ClientPersist() {};

//    static IOAuth2ClientPersist * FileCreate(class IIoMux * iomux, class IInstanceLog * const log, const char * path); // path with trailing slash
    static IOAuth2ClientPersist * DatabaseCreate(class IIoMux * iomux, class IInstanceLog * const log, class IDatabase * const database, const char * table);
    virtual void Initialize(UOAuth2ClientPersist * user) = 0;
    virtual void Persist(unsigned index, const char * value) = 0;
    virtual void Ready() = 0; // Underlying storage is ready
    virtual void Close() = 0; // Also call if underlying storage closed
};

class UOAuth2ClientTokenResult {
public:
    virtual ~UOAuth2ClientTokenResult() {};

    virtual void OAuth2ClientTokenResult(void * context, const char * token, enum OAuth2ClientTokenError error, const char * description) = 0;
};

class UOAuth2Client {
public:
    virtual ~UOAuth2Client() {};

    virtual void OAuth2ClientCloseComplete(class IOAuth2Client const * oauth2) = 0;
};

class IOAuth2Client {
public:
    virtual ~IOAuth2Client() {}

    static IOAuth2Client * Create(class IIoMux * iomux, class ISocketProvider * tcpSocketProvider, class ISocketProvider * tlsSocketProvider, class IInstanceLog * const log, class IDns * const dns, class IWebserverPlugin * const webserverPlugin, const char * resource, class IOAuth2ClientPersist * const persist, class UOAuth2Client * const user);
    virtual void SetRedirectURI(const char * redirectURI) = 0;
    virtual void Close() = 0;
    virtual void Token(UOAuth2ClientTokenResult * src, void * context, const char * credentials) = 0;
};

template <class OAuth2ClientTokenContext> class UOAuth2TokenSubclass : public UOAuth2ClientTokenResult {
    void OAuth2ClientTokenResult(void * context, const char * token, enum OAuth2ClientTokenError error, const char * description) { OAuth2ClientTokenResult((OAuth2ClientTokenContext *) context, token, error, description); };
public:
    virtual void OAuth2ClientTokenResult(OAuth2ClientTokenContext * context, const char * token, enum OAuth2ClientTokenError error, const char * description) = 0;
};

enum OAuth2Method {
    OAUTH2_CLIENT_SECRET_POST,
    OAUTH2_PRIVATE_KEY_JWT,
    OAUTH2_METHOD_COUNT
};

#define OAUTH2_AUTHORIZATION_HOST_MICROSOFT "login.microsoftonline.com"
#define OAUTH2_AUTHORIZATION_PATH_MICROSOFT "/oauth2/v2.0/authorize"
#define OAUTH2_TOKEN_HOST_MICROSOFT         "login.microsoftonline.com"
#define OAUTH2_TOKEN_PATH_MICROSOFT         "/oauth2/v2.0/token"

#define OAUTH2_AUTHORIZATION_HOST_GOOGLE    "accounts.google.com"
#define OAUTH2_AUTHORIZATION_PATH_GOOGLE    "/o/oauth2/auth"
#define OAUTH2_TOKEN_HOST_GOOGLE            "oauth2.googleapis.com"
#define OAUTH2_TOKEN_PATH_GOOGLE            "/token"

#define OAUTH2_TOKEN_HOST_HUAWEI            "oauth-login.cloud.huawei.com"
#define OAUTH2_TOKEN_PATH_HUAWEI            "/oauth2/v3/token"

#define OAUTH2_CREDENTIALS_TOKEN_ENDPOINT 0
#define OAUTH2_CREDENTIALS_STRINGS(m) (((m) == OAUTH2_PRIVATE_KEY_JWT) ? 5 : 6)
#define OAUTH2_CREDENTIALS_STRINGS_MAX  6

typedef struct OAuth2Credentials_ {
    enum OAuth2Method method;
    union {
        const char * strings[OAUTH2_CREDENTIALS_STRINGS_MAX];
        struct {
            const char * token_endpoint; const char * client_id; const char * username; const char * scope; const char * client_secret; const char * password;
        } client_secret_post;
        struct {
            const char * token_endpoint; const char * client_email; const char * private_key_id; const char * scope; const char * private_key;
        } private_key_jwt;
    } m;
} OAuth2Credentials;

unsigned OAuth2CredentialsString(char * buffer, unsigned size, const OAuth2Credentials * credentials);
enum OAuth2Method OAuth2CredentialsMethod(const char * string);
unsigned OAuth2CredentialsParse(const char * string, OAuth2Credentials * credentials);
unsigned OAuth2CredentialsIntersect(char * string, OAuth2Credentials * credentials);
