Have external login always make an API request to get the authorization URL

This makes the OIDC lookup lazy, ensuring that the rest of the registry and app continues working even if one OIDC provider goes down.
This commit is contained in:
Joseph Schorr 2017-01-23 19:06:19 -05:00
parent fda203e4d7
commit a9791ea419
9 changed files with 128 additions and 49 deletions

View file

@ -11,7 +11,9 @@ from peewee import IntegrityError
import features
from app import app, billing as stripe, authentication, avatar, user_analytics, all_queues
from app import (app, billing as stripe, authentication, avatar, user_analytics, all_queues,
oauth_login)
from auth import scopes
from auth.auth_context import get_authenticated_user
from auth.permissions import (AdministerOrganizationPermission, CreateRepositoryPermission,
@ -24,11 +26,12 @@ from endpoints.api import (ApiResource, nickname, resource, validate_json_reques
query_param, require_scope, format_date, show_if,
require_fresh_login, path_param, define_json_response,
RepositoryParamResource, page_support)
from endpoints.exception import NotFound, InvalidToken
from endpoints.exception import NotFound, InvalidToken, InvalidRequest, DownstreamIssue
from endpoints.api.subscribe import subscribe
from endpoints.common import common_login
from endpoints.csrf import generate_csrf_token, OAUTH_CSRF_TOKEN_NAME
from endpoints.decorators import anon_allowed
from oauth.oidc import DiscoveryFailureException
from util.useremails import (send_confirmation_email, send_recovery_email, send_change_email,
send_password_changed, send_org_recovery_email)
from util.names import parse_single_urn
@ -692,14 +695,50 @@ class Signout(ApiResource):
return {'success': True}
@resource('/v1/externaltoken')
@resource('/v1/externallogin/<service_id>')
@internal_only
class GenerateExternalToken(ApiResource):
""" Resource for generating a token for external login. """
@nickname('generateExternalLoginToken')
def post(self):
""" Generates a CSRF token explicitly for OIDC/OAuth-associated login. """
return {'token': generate_csrf_token(OAUTH_CSRF_TOKEN_NAME)}
class ExternalLoginInformation(ApiResource):
""" Resource for both setting a token for external login and returning its authorization
url.
"""
schemas = {
'GetLogin': {
'type': 'object',
'description': 'Information required to an retrieve external login URL.',
'required': [
'kind',
],
'properties': {
'kind': {
'type': 'string',
'description': 'The kind of URL',
'enum': ['login', 'attach'],
},
},
},
}
@nickname('retrieveExternalLoginAuthorizationUrl')
@anon_allowed
@validate_json_request('GetLogin')
def post(self, service_id):
""" Generates the auth URL and CSRF token explicitly for OIDC/OAuth-associated login. """
login_service = oauth_login.get_service(service_id)
if login_service is None:
raise InvalidRequest()
csrf_token = generate_csrf_token(OAUTH_CSRF_TOKEN_NAME)
kind = request.get_json()['kind']
redirect_suffix = '/attach' if kind == 'attach' else ''
try:
login_scopes = login_service.get_login_scopes()
auth_url = login_service.get_auth_url(app.config, redirect_suffix, csrf_token, login_scopes)
return {'auth_url': auth_url}
except DiscoveryFailureException as dfe:
logger.exception('Could not discovery OAuth endpoint information')
raise DownstreamIssue(dfe.message)
@resource('/v1/detachexternal/<service_id>')