Skip to content

Commit

Permalink
Update iddawc_jwt_profile and add doc
Browse files Browse the repository at this point in the history
  • Loading branch information
babelouest committed Apr 8, 2022
1 parent a1c707a commit 4e406e2
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 18 deletions.
32 changes: 32 additions & 0 deletions example_callbacks/iddawc_jwt_profile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Token validation for resource service based on [Ulfius](https://github.com/babelouest/ulfius) framework

These files contain an authentication callback for Ulfius framework to validate a [JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens Draft 10](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-10) with the correct scope. It also verifies DPoP header and its conformity with the access_token, according to [OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP) Draft 07](https://www.ietf.org/archive/id/draft-ietf-oauth-dpop-07.html).

[rhonabwy](https://github.com/babelouest/rhonabwy) and [iddawc](https://github.com/babelouest/iddawc) are required.

To use it, you must create a `struct _iddawc_resource_config` and initialize with the parameters:

```C
int i_jwt_profile_access_token_init_config(struct _iddawc_resource_config * config, // The config structure
unsigned short method, // method to retrieve the access token, vlues are G_METHOD_HEADER, G_METHOD_BODY or G_METHOD_URL for the access_token location, see https://tools.ietf.org/html/rfc6750
const char * realm, // Optional, a realm value that will be sent back to the client
const char * aud, // Optional, the aud value to check in the claims
const char * oauth_scope, // Scope values required by the resource, multiple values must be separated by a space character
const char * resource_url_root, // root url of the resource server, required if DPoP is used
time_t dpop_max_iat); // Maximum age in seconds for the DPoP iat value
```
Then, load the OIDC configuration url, or manually setup the public keys to validate the signatures:
```C
int i_jwt_profile_access_token_load_config(struct _iddawc_resource_config * config, const char * config_url, int verify_cert);
int i_jwt_profile_access_token_load_jwks(struct _iddawc_resource_config * config, json_t * j_jwks, const char * iss);
```

Then, you use `callback_check_jwt_profile_access_token` as authentication callback for your ulfius endpoints that need to validate an access_token, example:

```C
struct _iddawc_resource_config config;
ulfius_add_endpoint_by_val(instance, "GET", "/api", "/resource/*", &callback_check_jwt_profile_access_token, (void*)&config);
```
31 changes: 20 additions & 11 deletions example_callbacks/iddawc_jwt_profile/iddawc_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* Copyright 2021-2022 Nicolas Mora <mail@babelouest.org>
*
* Version 20220326
* Version 20220408
*
* The MIT License (MIT)
*
Expand Down Expand Up @@ -36,6 +36,18 @@

#include "iddawc_resource.h"

static const char * get_auth_header_token(const char * auth_header, int * is_header_dpop) {
if (0 == o_strncmp(HEADER_PREFIX_BEARER, auth_header, HEADER_PREFIX_BEARER_LEN)) {
*is_header_dpop = 0;
return auth_header + HEADER_PREFIX_BEARER_LEN;
} else if (0 == o_strncmp(HEADER_PREFIX_DPOP, auth_header, HEADER_PREFIX_DPOP_LEN)) {
*is_header_dpop = 1;
return auth_header + HEADER_PREFIX_DPOP_LEN;
} else {
return NULL;
}
}

/**
* Validates if an access_token grants has a valid scope
* return the final scope list on success
Expand Down Expand Up @@ -85,17 +97,15 @@ int jwt_profile_access_token_check_scope(struct _iddawc_resource_config * config
int callback_check_jwt_profile_access_token (const struct _u_request * request, struct _u_response * response, void * user_data) {
struct _iddawc_resource_config * config = (struct _iddawc_resource_config *)user_data;
json_t * j_access_token = NULL;
int res = U_CALLBACK_UNAUTHORIZED, res_validity;
const char * token_value = NULL;
int res = U_CALLBACK_UNAUTHORIZED, res_validity, is_header_dpop = 0;
const char * token_value = NULL, * dpop = u_map_get_case(request->map_header, HEADER_DPOP);
char * response_value = NULL, * htu;

if (config != NULL) {
switch (config->method) {
case I_METHOD_HEADER:
if (u_map_get_case(request->map_header, HEADER_AUTHORIZATION) != NULL) {
if (o_strstr(u_map_get_case(request->map_header, HEADER_AUTHORIZATION), HEADER_PREFIX_BEARER) == u_map_get_case(request->map_header, HEADER_AUTHORIZATION)) {
token_value = u_map_get_case(request->map_header, HEADER_AUTHORIZATION) + o_strlen(HEADER_PREFIX_BEARER);
}
token_value = get_auth_header_token(u_map_get_case(request->map_header, HEADER_AUTHORIZATION), &is_header_dpop);
}
break;
case I_METHOD_BODY:
Expand All @@ -121,8 +131,8 @@ int callback_check_jwt_profile_access_token (const struct _u_request * request,
u_map_put(response->map_header, HEADER_RESPONSE, response_value);
o_free(response_value);
} else {
if (!o_strnullempty(json_string_value(json_object_get(json_object_get(j_access_token, "cnf"), "jkt")))) {
htu = msprintf("%s%s", config->resource_url_root, request->url_path);
if (is_header_dpop && json_object_get(json_object_get(j_access_token, "cnf"), "jkt") != NULL && dpop != NULL) {
htu = msprintf("%s%s", config->resource_url_root, request->url_path+1);
if (i_verify_dpop_proof(u_map_get(request->map_header, I_HEADER_DPOP), request->http_verb, htu, config->dpop_max_iat, json_string_value(json_object_get(json_object_get(j_access_token, "cnf"), "jkt")), token_value) == I_OK) {
res = U_CALLBACK_CONTINUE;
if (ulfius_set_response_shared_data(response, json_deep_copy(j_access_token), (void (*)(void *))&json_decref) != U_OK) {
Expand All @@ -134,7 +144,7 @@ int callback_check_jwt_profile_access_token (const struct _u_request * request,
o_free(response_value);
}
o_free(htu);
} else {
} else if (!is_header_dpop && json_object_get(json_object_get(j_access_token, "cnf"), "jkt") == NULL && dpop == NULL) {
res = U_CALLBACK_CONTINUE;
if (ulfius_set_response_shared_data(response, json_deep_copy(j_access_token), (void (*)(void *))&json_decref) != U_OK) {
res = U_CALLBACK_ERROR;
Expand Down Expand Up @@ -162,7 +172,7 @@ int callback_check_jwt_profile_access_token (const struct _u_request * request,
return res;
}

int i_jwt_profile_access_token_init_config(struct _iddawc_resource_config * config, unsigned short method, const char * realm, const char * aud, const char * oauth_scope, const char * resource_url_root, unsigned short accept_client_token, time_t dpop_max_iat) {
int i_jwt_profile_access_token_init_config(struct _iddawc_resource_config * config, unsigned short method, const char * realm, const char * aud, const char * oauth_scope, const char * resource_url_root, time_t dpop_max_iat) {
int ret;
pthread_mutexattr_t mutexattr;

Expand All @@ -172,7 +182,6 @@ int i_jwt_profile_access_token_init_config(struct _iddawc_resource_config * conf
config->aud = o_strdup(aud);
config->oauth_scope = o_strdup(oauth_scope);
config->resource_url_root = o_strdup(resource_url_root);
config->accept_client_token = accept_client_token;
config->dpop_max_iat = dpop_max_iat;

if ((config->session = o_malloc(sizeof(struct _i_session))) != NULL) {
Expand Down
17 changes: 10 additions & 7 deletions example_callbacks/iddawc_jwt_profile/iddawc_resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* Copyright 2021-2022 Nicolas Mora <mail@babelouest.org>
*
* Version 20220326
* Version 20220408
*
* The MIT License (MIT)
*
Expand Down Expand Up @@ -41,11 +41,14 @@
#define I_METHOD_BODY 1
#define I_METHOD_URL 2

#define HEADER_PREFIX_BEARER "Bearer "
#define HEADER_RESPONSE "WWW-Authenticate"
#define HEADER_AUTHORIZATION "Authorization"
#define BODY_URL_PARAMETER "access_token"
#define HEADER_DPOP "DPoP"
#define HEADER_PREFIX_BEARER "Bearer "
#define HEADER_PREFIX_BEARER_LEN 7
#define HEADER_PREFIX_DPOP "DPoP "
#define HEADER_PREFIX_DPOP_LEN 5
#define HEADER_RESPONSE "WWW-Authenticate"
#define HEADER_AUTHORIZATION "Authorization"
#define BODY_URL_PARAMETER "access_token"
#define HEADER_DPOP "DPoP"

struct _iddawc_resource_config {
unsigned short method;
Expand All @@ -70,7 +73,7 @@ int jwt_profile_access_token_check_scope(struct _iddawc_resource_config * config
*/
int callback_check_jwt_profile_access_token (const struct _u_request * request, struct _u_response * response, void * user_data);

int i_jwt_profile_access_token_init_config(struct _iddawc_resource_config * config, unsigned short method, const char * realm, const char * aud, const char * oauth_scope, const char * resource_url_root, unsigned short accept_client_token, time_t dpop_max_iat);
int i_jwt_profile_access_token_init_config(struct _iddawc_resource_config * config, unsigned short method, const char * realm, const char * aud, const char * oauth_scope, const char * resource_url_root, time_t dpop_max_iat);

int i_jwt_profile_access_token_load_config(struct _iddawc_resource_config * config, const char * config_url, int verify_cert);

Expand Down

0 comments on commit 4e406e2

Please sign in to comment.