Skip to content

Commit f7723c6

Browse files
author
Hans Zandbelt
committed
support 3rd-party initiated login as defined in the spec
see: http://openid.net/specs/openid-connect-core-1_0.html section: 4. Initiating Login from a Third Party
1 parent 9c9be08 commit f7723c6

File tree

8 files changed

+80
-32
lines changed

8 files changed

+80
-32
lines changed

ChangeLog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
6/12/2014
2+
- support third-party-initiated login as defined in the spec
3+
14
6/6/2014
25
- more changes for Debian packaging (1.5-3)
36

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,18 +162,19 @@ And the related **mod_auth_openidc** Apache config section:
162162
If you do not want to use the internal discovery page (you really shouldn't...), you
163163
can have the user being redirected to an external discovery page by setting
164164
`OIDCDiscoveryURL`. That URL will be accessed with 2 parameters, `oidc_callback` and
165-
`oidc_return` (both URLs). The `oidc_return` parameter value needs to be returned to the
166-
`oidc_callback` URL (again in the `oidc_return parameter`) together with an
167-
`oidc_provider` parameter that contains the URL-encoded issuer value of the
165+
`target_link_uri` (both URLs). The `target_link_uri` parameter value needs to be returned to the
166+
`oidc_callback` URL (again in the `target_link_uri parameter`) together with an
167+
`iss` parameter that contains the URL-encoded issuer value of the
168168
selected Provider, or a URL-encoded account name for OpenID Connect Discovery
169169
purposes (aka. e-mail style identifier), or a domain name.
170170

171171
Sample callback:
172172

173-
${oidc_callback}?oidc_return=${oidc_return}&oidc_provider=[${issuer}|${domain}|${e-mail-style-account-name}]
173+
<oidc_callback>?target_link_uri=<target_link_uri>&iss=[<issuer>|<domain>|<e-mail-style-account-name>]
174174

175-
This is also the way of kicking off SSO to a specific provider from an
176-
external application/site when multiple OPs have been configured.
175+
This is also the OpenID Connect specified way of triggering 3rd party initiated SSO
176+
to a specific provider when multiple OPs have been configured. In that case the callback
177+
may also contain a "login_hint" parameter with the login identifier the user might use to log in.
177178

178179

179180
###Sample Config for PingFederate OpenID Connect & OAuth 2.0

auth_openidc.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ OIDCMetadataDir /var/cache/apache2/mod_auth_openidc/metadata
229229
# When not defined the bare-bones internal OP Discovery page is used.
230230
#OIDCDiscoverURL <discovery-url>
231231

232+
# (Optional)
233+
# Defines a default URL to be used in case of 3rd-party or OP initiated
234+
# SSO when no explicit target_link_uri has been provided.
235+
# When not default, 3rd-party SSO must be done with a specified \"target_link_uri\" parameter.
236+
#OIDCDefaultURL <default-url>
237+
232238
# (Optional)
233239
# Extra parameters that will be sent along with the Authorization Request.
234240
# These must be URL-query-encoded as in: "display=popup&prompt=consent" or

src/config.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ void *oidc_create_server_config(apr_pool_t *pool, server_rec *svr) {
473473

474474
c->redirect_uri = NULL;
475475
c->discover_url = NULL;
476+
c->default_url = NULL;
476477
c->public_keys = NULL;
477478
c->private_keys = NULL;
478479

@@ -555,6 +556,8 @@ void *oidc_merge_server_config(apr_pool_t *pool, void *BASE, void *ADD) {
555556
add->redirect_uri != NULL ? add->redirect_uri : base->redirect_uri;
556557
c->discover_url =
557558
add->discover_url != NULL ? add->discover_url : base->discover_url;
559+
c->default_url =
560+
add->default_url != NULL ? add->default_url : base->default_url;
558561
c->public_keys =
559562
add->public_keys != NULL ? add->public_keys : base->public_keys;
560563
c->private_keys =
@@ -1249,6 +1252,10 @@ const command_rec oidc_config_cmds[] = {
12491252
(void *)APR_OFFSETOF(oidc_cfg, discover_url),
12501253
RSRC_CONF,
12511254
"Defines an external IDP Discovery page"),
1255+
AP_INIT_TAKE1("OIDCDefaultURL", oidc_set_string_slot,
1256+
(void *)APR_OFFSETOF(oidc_cfg, default_url),
1257+
RSRC_CONF,
1258+
"Defines the default URL where the user is directed to in case of 3rd-party initiated SSO."),
12521259
AP_INIT_TAKE1("OIDCCookieDomain",
12531260
oidc_set_cookie_domain, NULL, RSRC_CONF,
12541261
"Specify domain element for OIDC session cookie."),

src/metadata.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,8 @@ static apr_byte_t oidc_metadata_client_get(request_rec *r, oidc_cfg *cfg,
848848
provider->userinfo_encrypted_response_enc);
849849
}
850850

851+
apr_table_addn(params, "initiate_login_uri",
852+
cfg->redirect_uri);
851853
}
852854

853855
/* try and get it from there, checking it and storing it if successful */

src/mod_auth_openidc.c

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,13 @@ static apr_byte_t oidc_unsolicited_proto_state(request_rec *r, oidc_cfg *c,
280280
char *target_uri = NULL;
281281
apr_jwt_get_string(r->pool, &jwt->payload.value, "target_uri", &target_uri);
282282
if (target_uri == NULL) {
283-
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
284-
"oidc_unsolicited_proto_state: no \"target_uri\" claim could be retrieved from JWT state, aborting");
285-
apr_jwt_destroy(jwt);
286-
return FALSE;
283+
if (c->default_url == NULL) {
284+
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
285+
"oidc_unsolicited_proto_state: no \"target_uri\" claim could be retrieved from JWT state and no OIDCDefaultURL is set, aborting");
286+
apr_jwt_destroy(jwt);
287+
return FALSE;
288+
}
289+
target_uri = c->default_url;
287290
}
288291

289292
if (c->metadata_dir != NULL) {
@@ -1082,7 +1085,7 @@ static int oidc_discovery(request_rec *r, oidc_cfg *cfg) {
10821085
* authenticate the user to the selected OP, if the OP is not selected yet perform discovery first
10831086
*/
10841087
static int oidc_authenticate_user(request_rec *r, oidc_cfg *c,
1085-
oidc_provider_t *provider, const char *original_url) {
1088+
oidc_provider_t *provider, const char *original_url, const char *login_hint) {
10861089

10871090
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
10881091
"oidc_authenticate_user: entering");
@@ -1161,7 +1164,7 @@ static int oidc_authenticate_user(request_rec *r, oidc_cfg *c,
11611164

11621165
/* send off to the OpenID Connect Provider */
11631166
// TODO: maybe show intermediate/progress screen "redirecting to"
1164-
return oidc_proto_authorization_request(r, provider, c->redirect_uri, state, &proto_state);
1167+
return oidc_proto_authorization_request(r, provider, login_hint, c->redirect_uri, state, &proto_state);
11651168
}
11661169

11671170
/*
@@ -1170,40 +1173,56 @@ static int oidc_authenticate_user(request_rec *r, oidc_cfg *c,
11701173
static apr_byte_t oidc_is_discovery_response(request_rec *r, oidc_cfg *cfg) {
11711174
/*
11721175
* prereq: this is a call to the configured redirect_uri, now see if:
1173-
* the OIDC_RT_PARAM_NAME parameter is present and
1174-
* the OIDC_DISC_ACCT_PARAM or OIDC_DISC_OP_PARAM is present
1176+
* the OIDC_DISC_OP_PARAM is present
11751177
*/
1176-
return (oidc_util_request_has_parameter(r, OIDC_DISC_RT_PARAM)
1177-
&& (oidc_util_request_has_parameter(r, OIDC_DISC_OP_PARAM)));
1178+
return oidc_util_request_has_parameter(r, OIDC_DISC_OP_PARAM);
11781179
}
11791180

11801181
/*
11811182
* handle a response from an IDP discovery page
11821183
*/
11831184
static int oidc_handle_discovery_response(request_rec *r, oidc_cfg *c) {
11841185

1185-
/* variables to hold the values (original_url+issuer or original_url+acct) returned in the response */
1186-
char *issuer = NULL, *original_url = NULL;
1186+
/* variables to hold the values returned in the response */
1187+
char *issuer = NULL, *target_link_uri = NULL, *login_hint = NULL;
11871188
oidc_provider_t *provider = NULL;
11881189

11891190
oidc_util_get_request_parameter(r, OIDC_DISC_OP_PARAM, &issuer);
1190-
oidc_util_get_request_parameter(r, OIDC_DISC_RT_PARAM, &original_url);
1191+
oidc_util_get_request_parameter(r, OIDC_DISC_RT_PARAM, &target_link_uri);
1192+
oidc_util_get_request_parameter(r, OIDC_DISC_LH_PARAM, &login_hint);
11911193

11921194
// TODO: trim issuer/accountname/domain input and do more input validation
11931195

11941196
ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r,
1195-
"oidc_handle_discovery_response: issuer=\"%s\", original_url=\"%s\"",
1196-
issuer, original_url);
1197+
"oidc_handle_discovery_response: issuer=\"%s\", target_link_uri=\"%s\", login_hint=\"%s\"",
1198+
issuer, target_link_uri, login_hint);
11971199

1198-
if ((issuer == NULL) || (original_url == NULL)) {
1200+
if (issuer == NULL) {
11991201
return oidc_util_http_sendstring(r,
12001202
"mod_auth_openidc: wherever you came from, it sent you here with the wrong parameters...",
12011203
HTTP_INTERNAL_SERVER_ERROR);
12021204
}
12031205

1206+
if (target_link_uri == NULL) {
1207+
if (c->default_url == NULL) {
1208+
return oidc_util_http_sendstring(r,
1209+
"mod_auth_openidc: 3rd party initiated SSO to this module without specifying a \"target_link_uri\" parameter is not possible because OIDCDefaultURL is not set.",
1210+
HTTP_INTERNAL_SERVER_ERROR);
1211+
}
1212+
target_link_uri = c->default_url;
1213+
}
1214+
1215+
// TODO: check that target_link_uri matches OIDCCookieDomain and/or OIDCRedirectURI
1216+
12041217
/* find out if the user entered an account name or selected an OP manually */
12051218
if (strstr(issuer, "@") != NULL) {
12061219

1220+
if (login_hint == NULL) {
1221+
login_hint = apr_pstrdup(r->pool, issuer);
1222+
//char *p = strstr(issuer, "@");
1223+
//*p = '\0';
1224+
}
1225+
12071226
/* got an account name as input, perform OP discovery with that */
12081227
if (oidc_proto_account_based_discovery(r, c, issuer, &issuer) == FALSE) {
12091228

@@ -1234,7 +1253,7 @@ static int oidc_handle_discovery_response(request_rec *r, oidc_cfg *c) {
12341253
&& (provider != NULL)) {
12351254

12361255
/* now we've got a selected OP, send the user there to authenticate */
1237-
return oidc_authenticate_user(r, c, provider, original_url);
1256+
return oidc_authenticate_user(r, c, provider, target_link_uri, login_hint);
12381257
}
12391258

12401259
/* something went wrong */
@@ -1406,7 +1425,7 @@ static int oidc_check_userid_openidc(request_rec *r, oidc_cfg *c) {
14061425
}
14071426

14081427
/* no session (regardless of whether it is main or sub-request), go and authenticate the user */
1409-
return oidc_authenticate_user(r, c, NULL, oidc_get_current_url(r, c));
1428+
return oidc_authenticate_user(r, c, NULL, oidc_get_current_url(r, c), NULL);
14101429
}
14111430

14121431
/*

src/mod_auth_openidc.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@ APLOG_USE_MODULE(auth_openidc);
8484
/* parameter name of the callback URL in the discovery response */
8585
#define OIDC_DISC_CB_PARAM "oidc_callback"
8686
/* parameter name of the OP provider selection in the discovery response */
87-
#define OIDC_DISC_OP_PARAM "oidc_provider"
87+
#define OIDC_DISC_OP_PARAM "iss"
8888
/* parameter name of the original URL in the discovery response */
89-
#define OIDC_DISC_RT_PARAM "oidc_return"
89+
#define OIDC_DISC_RT_PARAM "target_link_uri"
90+
/* parameter name of login hint in the discovery response */
91+
#define OIDC_DISC_LH_PARAM "login_hint"
9092

9193
/* value that indicates to use server-side cache based session tracking */
9294
#define OIDC_SESSION_TYPE_22_SERVER_CACHE 0
@@ -161,6 +163,8 @@ typedef struct oidc_cfg {
161163
char *redirect_uri;
162164
/* (optional) external OP discovery page */
163165
char *discover_url;
166+
/* (optional) default URL for 3rd-party initiated SSO */
167+
char *default_url;
164168

165169
/* public keys in JWK format, used by parters for encrypting JWTs sent to us */
166170
apr_hash_t *public_keys;
@@ -241,7 +245,7 @@ typedef struct oidc_proto_state {
241245
apr_time_t timestamp;
242246
} oidc_proto_state;
243247

244-
int oidc_proto_authorization_request(request_rec *r, struct oidc_provider_t *provider, const char *redirect_uri, const char *state, oidc_proto_state *proto_state);
248+
int oidc_proto_authorization_request(request_rec *r, struct oidc_provider_t *provider, const char *login_hint, const char *redirect_uri, const char *state, oidc_proto_state *proto_state);
245249
apr_byte_t oidc_proto_is_post_authorization_response(request_rec *r, oidc_cfg *cfg);
246250
apr_byte_t oidc_proto_is_redirect_authorization_response(request_rec *r, oidc_cfg *cfg);
247251
apr_byte_t oidc_proto_check_token_type(request_rec *r, oidc_provider_t *provider, const char *token_type);

src/proto.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ int oidc_proto_authorization_request_post_preserve(request_rec *r,
110110
* send an OpenID Connect authorization request to the specified provider
111111
*/
112112
int oidc_proto_authorization_request(request_rec *r,
113-
struct oidc_provider_t *provider, const char *redirect_uri,
113+
struct oidc_provider_t *provider, const char *login_hint, const char *redirect_uri,
114114
const char *state, oidc_proto_state *proto_state) {
115115

116116
/* log some stuff */
@@ -149,10 +149,11 @@ int oidc_proto_authorization_request(request_rec *r,
149149
authorization_request,
150150
oidc_util_escape_string(r, proto_state->response_mode));
151151

152-
/* preserve POSTed form parameters if enabled */
153-
if (apr_strnatcmp(proto_state->original_method, "form_post") == 0)
154-
return oidc_proto_authorization_request_post_preserve(r,
155-
authorization_request);
152+
/* add the login_hint if provided */
153+
if (login_hint != NULL)
154+
authorization_request = apr_psprintf(r->pool, "%s&login_hint=%s",
155+
authorization_request,
156+
oidc_util_escape_string(r, login_hint));
156157

157158
/* add any custom authorization request parameters if configured */
158159
if (provider->auth_request_params != NULL) {
@@ -161,6 +162,11 @@ int oidc_proto_authorization_request(request_rec *r,
161162
provider->auth_request_params);
162163
}
163164

165+
/* preserve POSTed form parameters if enabled */
166+
if (apr_strnatcmp(proto_state->original_method, "form_post") == 0)
167+
return oidc_proto_authorization_request_post_preserve(r,
168+
authorization_request);
169+
164170
/* add the redirect location header */
165171
apr_table_add(r->headers_out, "Location", authorization_request);
166172

0 commit comments

Comments
 (0)