def create_response_mode_response(redirect_uri, params, response_mode=None): if response_mode is None: response_mode = os.getenv('AUTHLIB_DEFAULT_RESPONSE_CODE', 'fragment') if response_mode == 'form_post': tpl = ( '<html><head><title>Authlib</title></head>' '<body onload="javascript:document.forms[0].submit()">' '<form method="post" action="{}">{}</form></body></html>' ) inputs = ''.join([ '<input type="hidden" name="{}" value="{}"/>'.format( quote_url(k), quote_url(v)) for k, v in params ]) body = tpl.format(quote_url(redirect_uri), inputs) return 200, body, [('Content-Type', 'text/html; charset=utf-8')] if response_mode == 'query': uri = add_params_to_uri(redirect_uri, params, fragment=False) elif response_mode == 'fragment': uri = add_params_to_uri(redirect_uri, params, fragment=True) else: raise InvalidRequestError('Invalid "response_mode" value') return 302, '', [('Location', uri)]
def test_invalid_request_parameters(self): self.prepare_data() handle = self.create_route() url = '/user' # case 1 request = self.factory.get(url) resp = handle(request) data = json.loads(to_unicode(resp.content)) self.assertEqual(data['error'], 'missing_required_parameter') self.assertIn('oauth_consumer_key', data['error_description']) # case 2 request = self.factory.get( add_params_to_uri(url, {'oauth_consumer_key': 'a'})) resp = handle(request) data = json.loads(to_unicode(resp.content)) self.assertEqual(data['error'], 'invalid_client') # case 3 request = self.factory.get( add_params_to_uri(url, {'oauth_consumer_key': 'client'})) resp = handle(request) data = json.loads(to_unicode(resp.content)) self.assertEqual(data['error'], 'missing_required_parameter') self.assertIn('oauth_token', data['error_description']) # case 4 request = self.factory.get( add_params_to_uri(url, { 'oauth_consumer_key': 'client', 'oauth_token': 'a' }) ) resp = handle(request) data = json.loads(to_unicode(resp.content)) self.assertEqual(data['error'], 'invalid_token') # case 5 request = self.factory.get( add_params_to_uri(url, { 'oauth_consumer_key': 'client', 'oauth_token': 'valid-token' }) ) resp = handle(request) data = json.loads(to_unicode(resp.content)) self.assertEqual(data['error'], 'missing_required_parameter') self.assertIn('oauth_timestamp', data['error_description'])
def absolute_login_url(provider_id, fence_idp=None, shib_idp=None): """ Args: provider_id (str): provider to log in with; an IDP_URL_MAP key. fence_idp (str, optional): if provider_id is "fence" (multi-tenant Fence setup), fence_idp can be any of the providers supported by the other Fence. If not specified, will default to NIH login. shib_idp (str, optional): if provider_id is "fence" and fence_idp is "shibboleth", shib_idp can be any Shibboleth/ InCommon provider. If not specified, will default to NIH login. Returns: str: login URL for this provider, including extra query parameters if fence_idp and/or shib_idp are specified. """ try: base_url = config["BASE_URL"].rstrip("/") login_url = base_url + "/login/{}".format( IDP_URL_MAP[provider_id]) except KeyError as e: raise InternalError( "identity provider misconfigured: {}".format(str(e))) params = {} if fence_idp: params["idp"] = fence_idp if shib_idp: params["shib_idp"] = shib_idp login_url = add_params_to_uri(login_url, params) return login_url
def test_invalid_request(self): self.prepare_data() headers = self.create_basic_header( 'password-client', 'password-secret' ) rv = self.client.get(add_params_to_uri('/oauth/token', { 'grant_type': 'password', }), headers=headers) resp = json.loads(rv.data) self.assertEqual(resp['error'], 'invalid_grant') rv = self.client.post('/oauth/token', data={ 'grant_type': 'password', }, headers=headers) resp = json.loads(rv.data) self.assertEqual(resp['error'], 'invalid_request') rv = self.client.post('/oauth/token', data={ 'grant_type': 'password', 'username': '******', }, headers=headers) resp = json.loads(rv.data) self.assertEqual(resp['error'], 'invalid_request') rv = self.client.post('/oauth/token', data={ 'grant_type': 'password', 'username': '******', 'password': '******', }, headers=headers) resp = json.loads(rv.data) self.assertEqual(resp['error'], 'invalid_request')
def get(self): """Handle ``GET /login/fence``.""" oauth2_redirect_uri = flask.current_app.fence_client.client_kwargs.get( "redirect_uri") redirect_url = flask.request.args.get("redirect") if redirect_url: validate_redirect(redirect_url) flask.session["redirect"] = redirect_url ( authorization_url, state, ) = flask.current_app.fence_client.generate_authorize_redirect( oauth2_redirect_uri, prompt="login") # add idp parameter to the authorization URL if "idp" in flask.request.args: idp = flask.request.args["idp"] flask.session["fence_idp"] = idp params = {"idp": idp} # if requesting to login through Shibboleth, also add shib_idp # parameter to the authorization URL if idp == "shibboleth" and "shib_idp" in flask.request.args: shib_idp = flask.request.args["shib_idp"] params["shib_idp"] = shib_idp flask.session["shib_idp"] = shib_idp authorization_url = add_params_to_uri(authorization_url, params) flask.session["state"] = state return flask.redirect(authorization_url)
def add_to_uri(token, uri): """Add a Bearer Token to the request URI. Not recommended, use only if client can't use authorization header or body. http://www.example.com/path?access_token=h480djs93hd8 """ return add_params_to_uri(uri, [('access_token', token)])
def create_authorization_response(self, grant_user): state = self.request.state if grant_user: self.request.user = grant_user client = self.request.client token = self.generate_token(client, self.GRANT_TYPE, scope=self.request.scope, include_refresh_token=False) if self.request.response_type == 'id_token': token = { 'expires_in': token['expires_in'], 'scope': token['scope'], } token = self.process_token(token, self.request) else: log.debug('Grant token {!r} to {!r}'.format(token, client)) self.server.save_token(token, self.request) token = self.process_token(token, self.request) params = [(k, token[k]) for k in token] if state: params.append(('state', state)) else: error = AccessDeniedError(state=state) params = error.get_body() uri = add_params_to_uri(self.redirect_uri, params, fragment=True) headers = [('Location', uri)] return 302, '', headers
def create_valid_authorization_response(self, request, grant_user): """Validate authorization request and create authorization response. :param request: OAuth2Request instance. :param grant_user: if granted, it is resource owner. If denied, it is None. :returns: (status_code, body, headers) """ # TODO: rename it to `create_authorization_response` in v0.8 try: grant = self.get_authorization_grant(request) except InvalidGrantError as error: return error(translations=self.get_translations(), error_uris=self.get_error_uris()) try: grant.validate_authorization_request() return grant.create_authorization_response(grant_user) except OAuth2Error as error: if grant.redirect_uri: params = error.get_body() loc = add_params_to_uri(grant.redirect_uri, params) headers = [('Location', loc)] return 302, '', headers return error(translations=self.get_translations(), error_uris=self.get_error_uris())
def __call__(self, translations=None, error_uris=None): if self.redirect_uri: params = self.get_body() loc = add_params_to_uri( self.redirect_uri, params, self.redirect_fragment) return 302, '', [('Location', loc)] return super(OAuth2Error, self).__call__(translations, error_uris)
def create_authorization_response(self, request=None, grant_user=None): """Validate authorization request and create authorization response. :param request: OAuth2Request instance. :param grant_user: if granted, it is resource owner. If denied, it is None. :returns: Response """ request = self.create_oauth2_request(request) try: grant = self.get_authorization_grant(request) except InvalidGrantError as error: return self.handle_error_response(request, error) try: grant.validate_authorization_request() args = grant.create_authorization_response(grant_user) return self.handle_response(*args) except OAuth2Error as error: if grant.redirect_uri: params = error.get_body() loc = add_params_to_uri(grant.redirect_uri, params) headers = [('Location', loc)] return self.handle_response(302, '', headers) return self.handle_error_response(request, error)
def create_authorization_response(self, redirect_uri, grant_user): """If the resource owner grants the access request, the authorization server issues an authorization code and delivers it to the client by adding the following parameters to the query component of the redirection URI using the "application/x-www-form-urlencoded" format. Per `Section 4.1.2`_. code REQUIRED. The authorization code generated by the authorization server. The authorization code MUST expire shortly after it is issued to mitigate the risk of leaks. A maximum authorization code lifetime of 10 minutes is RECOMMENDED. The client MUST NOT use the authorization code more than once. If an authorization code is used more than once, the authorization server MUST deny the request and SHOULD revoke (when possible) all tokens previously issued based on that authorization code. The authorization code is bound to the client identifier and redirection URI. state REQUIRED if the "state" parameter was present in the client authorization request. The exact value received from the client. For example, the authorization server redirects the user-agent by sending the following HTTP response. .. code-block:: http HTTP/1.1 302 Found Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA &state=xyz .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc6749#section-4.1.2 :param redirect_uri: Redirect to the given URI for the authorization :param grant_user: if resource owner granted the request, pass this resource owner, otherwise pass None. :returns: (status_code, body, headers) """ state = self.request.state if grant_user: self.request.user = grant_user if hasattr(self, 'create_authorization_code'): # TODO: deprecate code = self.create_authorization_code(self.request.client, grant_user, self.request) else: code = self.generate_authorization_code() self.save_authorization_code(code, self.request) params = [('code', code)] if state: params.append(('state', state)) uri = add_params_to_uri(redirect_uri, params) headers = [('Location', uri)] return 302, '', headers else: raise AccessDeniedError(state=state, redirect_uri=redirect_uri)
def encode_none(client, method, uri, headers, body): if method == 'GET': uri = add_params_to_uri(uri, [('client_id', client.client_id)]) return uri, headers, body body = add_params_to_qs(body, [('client_id', client.client_id)]) if 'Content-Length' in headers: headers['Content-Length'] = str(len(body)) return uri, headers, body
def auth_client(client, method, uri, headers, body): uri = add_params_to_uri(uri, [ ('client_id', client.client_id), ('client_secret', client.client_secret), ]) uri = uri + '&' + body body = '' return uri, headers, body
def create_valid_authorization_response(self, request, grant_user=None): """Validate authorization request and create authorization response. Assume the endpoint for authorization request is ``https://photos.example.net/authorize``, the client redirects Jane's user-agent to the server's Resource Owner Authorization endpoint to obtain Jane's approval for accessing her private photos:: https://photos.example.net/authorize?oauth_token=hh5s93j4hdidpola The server requests Jane to sign in using her username and password and if successful, asks her to approve granting 'printer.example.com' access to her private photos. Jane approves the request and her user-agent is redirected to the callback URI provided by the client in the previous request (line breaks are for display purposes only):: http://printer.example.com/ready? oauth_token=hh5s93j4hdidpola&oauth_verifier=hfdp7dh39dks9884 :param request: OAuth1Request instance. :param grant_user: if granted, pass the grant user, otherwise None. :returns: (status_code, body, headers) """ # authorize endpoint should try catch this error self.validate_authorization_request(request) temporary_credentials = request.credential redirect_uri = temporary_credentials.get_redirect_uri() if not redirect_uri or redirect_uri == 'oob': client_id = temporary_credentials.get_client_id() client = self.get_client_by_id(client_id) redirect_uri = client.get_default_redirect_uri() if grant_user is None: error = AccessDeniedError() location = add_params_to_uri(redirect_uri, error.get_body()) return 302, '', [('Location', location)] request.grant_user = grant_user verifier = self.create_authorization_verifier(request) params = [ ('oauth_token', request.token), ('oauth_verifier', verifier) ] location = add_params_to_uri(redirect_uri, params) return 302, '', [('Location', location)]
def _add_extra_info(url, headers, body): params = {'site': site} api_key = session._kwargs.get('api_key') if hasattr(session, '_kwargs') else os.environ['STACKOVERFLOW_KEY'] api_filter = session._kwargs.get('api_filter') if hasattr(session, '_kwargs') else None if api_key: params['key'] = api_key if api_filter: params['filter'] = api_filter url = add_params_to_uri(url, params) return url, headers, body
def test_invalid_request_parameters(self): self.prepare_data() url = '/user' # case 1 rv = self.client.get(url) data = json.loads(rv.data) self.assertEqual(data['error'], 'missing_required_parameter') self.assertIn('oauth_consumer_key', data['error_description']) # case 2 rv = self.client.get( add_params_to_uri(url, {'oauth_consumer_key': 'a'})) data = json.loads(rv.data) self.assertEqual(data['error'], 'invalid_client') # case 3 rv = self.client.get( add_params_to_uri(url, {'oauth_consumer_key': 'client'})) data = json.loads(rv.data) self.assertEqual(data['error'], 'missing_required_parameter') self.assertIn('oauth_token', data['error_description']) # case 4 rv = self.client.get( add_params_to_uri(url, { 'oauth_consumer_key': 'client', 'oauth_token': 'a' }) ) data = json.loads(rv.data) self.assertEqual(data['error'], 'invalid_token') # case 5 rv = self.client.get( add_params_to_uri(url, { 'oauth_consumer_key': 'client', 'oauth_token': 'valid-token' }) ) data = json.loads(rv.data) self.assertEqual(data['error'], 'missing_required_parameter') self.assertIn('oauth_timestamp', data['error_description'])
def _add_extra_info(url, headers, body): params = {'site': site} api_key = session._kwargs.get('api_key') api_filter = session._kwargs.get('api_filter') if api_key: params['key'] = api_key if api_filter: params['filter'] = api_filter url = add_params_to_uri(url, params) return url, headers, body
def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None, scope=None, state=None, **kwargs): """Prepare the authorization grant request URI. The client constructs the request URI by adding the following parameters to the query component of the authorization endpoint URI using the ``application/x-www-form-urlencoded`` format: :param uri: The authorize endpoint to fetch "code" or "token". :param client_id: The client identifier as described in `Section 2.2`_. :param response_type: To indicate which OAuth 2 grant/flow is required, "code" and "token". :param redirect_uri: The client provided URI to redirect back to after authorization as described in `Section 3.1.2`_. :param scope: The scope of the access request as described by `Section 3.3`_. :param state: An opaque value used by the client to maintain state between the request and callback. The authorization server includes this value when redirecting the user-agent back to the client. The parameter SHOULD be used for preventing cross-site request forgery as described in `Section 10.12`_. :param kwargs: Extra arguments to embed in the grant/authorization URL. An example of an authorization code grant authorization URL:: /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb .. _`Section 2.2`: http://tools.ietf.org/html/rfc6749#section-2.2 .. _`Section 3.1.2`: http://tools.ietf.org/html/rfc6749#section-3.1.2 .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3 .. _`section 10.12`: http://tools.ietf.org/html/rfc6749#section-10.12 """ params = [('response_type', response_type), ('client_id', client_id)] if redirect_uri: params.append(('redirect_uri', redirect_uri)) if scope: params.append(('scope', list_to_scope(scope))) if state: params.append(('state', state)) for k in kwargs: if kwargs[k]: params.append((to_unicode(k), kwargs[k])) return add_params_to_uri(uri, params)
def test_consent_view(self): self.prepare_data() rv = self.client.get(add_params_to_uri('/oauth/authorize', { 'response_type': 'id_token', 'client_id': 'implicit-client', 'scope': 'openid profile', 'state': 'foo', 'redirect_uri': 'https://a.b/c', 'user_id': '1' })) self.assertIn(b'error=invalid_request', rv.data) self.assertIn(b'nonce', rv.data)
def _get_authorize_error_response(error, redirect_uri): """ Get error response as defined by OIDC spec. Args: error (authlib.oauth2.rfc6749.error.OAuth2Error): Specific Oauth2 error redirect_uri (str): Redirection url """ params = error.get_body() uri = add_params_to_uri(redirect_uri, params) headers = [("Location", uri)] response = flask.Response("", status=302, headers=headers) return response
def test_consent_view(self): self.prepare_data() rv = self.client.get( add_params_to_uri( "/oauth/authorize", { "response_type": "id_token", "client_id": "implicit-client", "scope": "openid profile", "state": "foo", "redirect_uri": "https://a.b/c", "user_id": "1", }, )) self.assertIn("error=invalid_request", rv.json()) self.assertIn("nonce", rv.json())
def create_authorization_response(self, grant_user): state = self.request.state if grant_user: self.request.user = grant_user client = self.request.client code = self.create_authorization_code(client, grant_user, self.request) params = [('code', code)] token = self.generate_token(client, 'implicit', scope=self.request.scope, include_refresh_token=False) response_types = self.request.response_type.split() if 'token' in response_types: log.debug('Grant token {!r} to {!r}'.format(token, client)) self.server.save_token(token, self.request) if 'id_token' in response_types: token = self.process_implicit_token( token, self.request, code) else: # response_type is "code id_token" token = { 'expires_in': token['expires_in'], 'scope': token['scope'] } token = self.process_implicit_token(token, self.request, code) params.extend([(k, token[k]) for k in token]) if state: params.append(('state', state)) else: error = AccessDeniedError(state=state) params = error.get_body() # http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes fragment = True response_mode = self.request.response_mode if response_mode and response_mode == 'query': fragment = False uri = add_params_to_uri(self.redirect_uri, params, fragment=fragment) headers = [('Location', uri)] return 302, '', headers
async def create_authorization_response(self, redirect_uri, grant_user): state = self.request.state if grant_user: self.request.user = grant_user code = self.generate_authorization_code() await self.save_authorization_code(code, self.request) params = [("code", code)] if state: params.append(("state", state)) uri = add_params_to_uri(redirect_uri, params) headers = [('Location', uri)] return 302, '', headers else: raise AccessDeniedError(state=state, redirect_uri=redirect_uri)
def create_authorization_url(self, url, request_token=None, **kwargs): """Create an authorization URL by appending request_token and optional kwargs to url. This is the second step in the OAuth 1 workflow. The user should be redirected to this authorization URL, grant access to you, and then be redirected back to you. The redirection back can either be specified during client registration or by supplying a callback URI per request. :param url: The authorization endpoint URL. :param request_token: The previously obtained request token. :param kwargs: Optional parameters to append to the URL. :returns: The authorization URL with new parameters embedded. """ kwargs['oauth_token'] = request_token or self._client.token if self._client.redirect_uri: kwargs['oauth_callback'] = self._client.redirect_uri return add_params_to_uri(url, kwargs.items())
def create_authorization_response(self, grant_user): state = self.request.state if grant_user: params = self._create_granted_params(grant_user) if state: params.append(('state', state)) else: error = AccessDeniedError(state=state) params = error.get_body() # http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes fragment = True response_mode = self.request.response_mode if response_mode and response_mode == 'query': fragment = False uri = add_params_to_uri(self.redirect_uri, params, fragment=fragment) headers = [('Location', uri)] return 302, '', headers
def create_valid_authorization_response(self, uri, user): """Validate authorization request and create authorization response. :param uri: requested URI string :param user: if granted, user is resource owner. If denied, it is None. :returns: (status_code, body, headers) """ try: grant = self.get_authorization_grant(uri) except InvalidGrantError as error: body = dict(error.get_body()) return error.status_code, body, error.get_headers() try: grant.validate_authorization_request() return grant.create_authorization_response(user) except OAuth2Error as error: params = error.get_body() loc = add_params_to_uri(grant.redirect_uri, params) headers = [('Location', loc)] return 302, '', headers
def create_authorization_response( self, request: Request, grant_user: typing.Optional[UserModelWrapper]) -> Response: error = "server_error" oauth2_request = self.create_oauth2_request(request) redirect_uri: typing.Optional[str] try: redirect_uri = self.get_redirect_uri_if_available(oauth2_request) except OAuth2Error as e: return PlainTextResponse(e.error, status_code=HTTP_400_BAD_REQUEST) if grant_user is None: error = "unauthorized_client" else: try: grant = self.get_authorization_grant(oauth2_request) redirect_uri = grant.validate_authorization_request() args = grant.create_authorization_response( redirect_uri, grant_user) return self.handle_response(*args) except InvalidGrantError as e: logger.debug(f"{e}", exc_info=True) error = "unsupported_response_type" except OAuth2Error as e: error = e.error except Exception as e: logger.error(f"unexpected exception: {e}", exc_info=True) params = { "error": error, } if oauth2_request.state is not None: params["state"] = oauth2_request.state if redirect_uri is not None: return RedirectResponse(add_params_to_uri(redirect_uri, params)) else: return PlainTextResponse(error, status_code=HTTP_400_BAD_REQUEST)
def create_endpoint_response(self, request): # https://tools.ietf.org/html/rfc8628#section-3.1 self.authenticate_client(request) self.server.validate_requested_scope(request.scope) device_code = self.generate_device_code() user_code = self.generate_user_code() verification_uri = self.get_verification_uri() verification_uri_complete = add_params_to_uri( verification_uri, [('user_code', user_code)]) data = { 'device_code': device_code, 'user_code': user_code, 'verification_uri': verification_uri, 'verification_uri_complete': verification_uri_complete, 'expires_in': self.EXPIRES_IN, 'interval': self.INTERVAL, } self.save_device_credential(request.client_id, request.scope, data) return 200, data, default_json_headers
def load_settings(app): """ load setttings from environment variables SECRET_KEY: app secret key to encrypt session cookies ENCRYPTION_KEY: encryption key to encrypt credentials in database POSTGRES_CREDS_FILE: JSON file with "db_username", "db_password", "db_host" and "db_database" keys SQLALCHEMY_DATABASE_URI: database connection uri. Overriden by POSTGRES_CREDS_FILE FENCE_BASE_URL: fence base url, eg: https://gen3_commons/user WTS_BASE_URL: base url for this workspace token service OIDC_CLIENT_ID: client id for the oidc client for this app OIDC_CLIENT_SECRET: client secret for the oidc client for this app AUTH_PLUGINS: a list of comma separate plugins, eg: k8s EXTERNAL_OIDC: config for additional oidc handshakes """ app.secret_key = get_var("SECRET_KEY") app.encrytion_key = Fernet(get_var("ENCRYPTION_KEY")) postgres_creds = get_var("POSTGRES_CREDS_FILE", "") if postgres_creds: with open(postgres_creds, "r") as f: creds = json.load(f) app.config[ "SQLALCHEMY_DATABASE_URI" ] = "postgresql://{db_username}:{db_password}@{db_host}:5432/{db_database}".format( **creds ) else: app.config["SQLALCHEMY_DATABASE_URI"] = get_var("SQLALCHEMY_DATABASE_URI") url = get_var("FENCE_BASE_URL") fence_base_url = url if url.endswith("/") else (url + "/") wts_base_url = get_var("WTS_BASE_URL") if not wts_base_url.endswith("/"): wts_base_url = wts_base_url + "/" wts_hostname = urlparse(wts_base_url).netloc.split(".")[0] oauth_config = { "client_id": get_var("OIDC_CLIENT_ID"), "client_secret": get_var("OIDC_CLIENT_SECRET"), "api_base_url": fence_base_url, "authorize_url": fence_base_url + "oauth2/authorize", "access_token_url": fence_base_url + "oauth2/token", "redirect_uri": wts_base_url + "oauth2/authorize", "scope": "openid data user", "state_prefix": "", } app.config["OIDC"] = {"default": oauth_config} for conf in get_var("EXTERNAL_OIDC", []): url = get_var("BASE_URL", secret_config=conf) fence_base_url = (url if url.endswith("/") else (url + "/")) + "user/" # can redirect authorize callbacks to a shared central authorizer redirect_uri = get_var("REDIRECT_URI", default="", secret_config=conf) state_prefix = wts_hostname or "" if not redirect_uri: redirect_uri = wts_base_url + "oauth2/authorize" state_prefix = "" for idp, idp_conf in conf.get("login_options", {}).items(): authorization_url = fence_base_url + "oauth2/authorize" authorization_url = add_params_to_uri( authorization_url, idp_conf.get("params", {}) ) app.config["OIDC"][idp] = { "client_id": get_var("OIDC_CLIENT_ID", secret_config=conf), "client_secret": get_var("OIDC_CLIENT_SECRET", secret_config=conf), "api_base_url": fence_base_url, "authorize_url": authorization_url, "access_token_url": fence_base_url + "oauth2/token", "redirect_uri": redirect_uri, "scope": "openid data user", "state_prefix": state_prefix, } app.config["SESSION_COOKIE_NAME"] = "wts" app.config["SESSION_COOKIE_SECURE"] = True app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
def authorize(*args, **kwargs): """ OIDC Authorization Endpoint From the OIDC Specification: 3.1.1. Authorization Code Flow Steps The Authorization Code Flow goes through the following steps. - Client prepares an Authentication Request containing the desired request parameters. - Client sends the request to the Authorization Server. - Authorization Server Authenticates the End-User. - Authorization Server obtains End-User Consent/Authorization. - Authorization Server sends the End-User back to the Client with an Authorization Code. - Client requests a response using the Authorization Code at the Token Endpoint. - Client receives a response that contains an ID Token and Access Token in the response body. - Client validates the ID token and retrieves the End-User's Subject Identifier. Args: *args: additional arguments **kwargs: additional keyword arguments """ need_authentication = False try: user = get_current_user() except Unauthorized: need_authentication = True if need_authentication or not user: redirect_url = config.get("BASE_URL") + flask.request.full_path params = {"redirect": redirect_url} login_url = config.get("DEFAULT_LOGIN_URL") idp = flask.request.args.get("idp") if idp: if idp not in IDP_URL_MAP or idp not in config["OPENID_CONNECT"]: raise UserError("idp {} is not supported".format(idp)) idp_url = IDP_URL_MAP[idp] login_url = "{}/login/{}".format(config.get("BASE_URL"), idp_url) login_url = add_params_to_uri(login_url, params) return flask.redirect(login_url) try: grant = server.validate_consent_request(end_user=user) except OAuth2Error as e: raise Unauthorized("{} failed to authorize".format(str(e))) client_id = grant.client.client_id with flask.current_app.db.session as session: client = session.query(Client).filter_by(client_id=client_id).first() # TODO: any way to get from grant? confirm = flask.request.form.get("confirm") or flask.request.args.get( "confirm") if client.auto_approve: confirm = "yes" if confirm is not None: response = _handle_consent_confirmation(user, confirm) # if it's a 302 for POST confirm, return 200 instead and include # redirect url in body because browser ajax POST doesn't follow # cross origin redirect if flask.request.method == "POST" and response.status_code == 302: return flask.jsonify({"redirect": response.headers["Location"]}) else: # no confirm param, so no confirmation has occured yet response = _authorize(user, grant, client) return response