Skip to content

Schemas

MyOAuth2Auth (OAuth2Authentication)

Temporary workaround until DRF 3.13 merges https://github.com/encode/django-rest-framework/pull/7516 and DOT's OAuth2Authentication gets updated to add openapi security schemes and security requirement objects.

Source code in myapp/schemas.py
class MyOAuth2Auth(OAuth2Authentication):
    """
    Temporary workaround until DRF 3.13 merges https://github.com/encode/django-rest-framework/pull/7516
    and DOT's OAuth2Authentication gets updated to add openapi security schemes and security requirement objects.
    """
    openapi_security_scheme_name = 'oauth2ForYou'

    @classmethod
    def openapi_security_scheme(cls):
        # N.B. Swagger UI still does not support OIDC, so always use OAuth2:
        scheme = {
            cls.openapi_security_scheme_name: {
                'type': 'oauth2',
                'description': 'OAuth 2.0 authentication',
            }
        }
        flows = {}
        if 'authorization_code' in settings.OAUTH2_CONFIG['grant_types_supported']:
            flows['authorizationCode'] = {
                'authorizationUrl': settings.OAUTH2_CONFIG['authorization_endpoint'],
                'tokenUrl': settings.OAUTH2_CONFIG['token_endpoint'],
                'refreshUrl': settings.OAUTH2_CONFIG['token_endpoint'],
                'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
        if 'implicit' in settings.OAUTH2_CONFIG['grant_types_supported']:
            flows['implicit'] = {
                'authorizationUrl': settings.OAUTH2_CONFIG['authorization_endpoint'],
                'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
        if 'client_credentials' in settings.OAUTH2_CONFIG['grant_types_supported']:
            flows['clientCredentials'] = {
                'tokenUrl': settings.OAUTH2_CONFIG['token_endpoint'],
                'refreshUrl': settings.OAUTH2_CONFIG['token_endpoint'],
                'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
        if 'password' in settings.OAUTH2_CONFIG['grant_types_supported']:
            flows['password'] = {
                'tokenUrl': settings.OAUTH2_CONFIG['token_endpoint'],
                'refreshUrl': settings.OAUTH2_CONFIG['token_endpoint'],
                'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
        scheme[cls.openapi_security_scheme_name]['flows'] = flows

        # TODO: add JWT and SAML2 bearer

        # content = []
        # permission_classes can be a direct list of classes, or instances of Operands, etc.
        # for perm in cls.view.permission_classes:
        #     if (isinstance(perm(), TokenMatchesOASRequirements)
        #             or cls._drf_conditional_contains(perm(), TokenMatchesOASRequirements)
        #             or cls._rest_cond_contains(perm(), TokenMatchesOASRequirements)):
        #         alt_scopes = cls.view.required_alternate_scopes
        #         if method not in alt_scopes:
        #             continue
        #         for scopes in alt_scopes[method]:
        #             content.append({'oauth': scopes})
        #
        return scheme

    @classmethod
    def openapi_security_requirement(cls, view, method):
        """
        OAuth2 is the only OAS security requirement object that fills in the list of required scopes

        Args:
            view: used to get to the `required_alternate_scopes` attribute
            method: HTTP method name used as key for `required_alternate_scopes`

        Returns: dict of scopes
        """
        scopes = []
        if hasattr(view, 'required_alternate_scopes'):
            if method in view.required_alternate_scopes:
                for alt in view.required_alternate_scopes[method]:
                    scopes.append({cls.openapi_security_scheme_name: alt})
        return scopes

openapi_security_scheme_name

openapi_security_requirement(view, method) classmethod

OAuth2 is the only OAS security requirement object that fills in the list of required scopes

Parameters:

Name Type Description Default
view

used to get to the required_alternate_scopes attribute

required
method

HTTP method name used as key for required_alternate_scopes

required

Returns: dict of scopes

Source code in myapp/schemas.py
@classmethod
def openapi_security_requirement(cls, view, method):
    """
    OAuth2 is the only OAS security requirement object that fills in the list of required scopes

    Args:
        view: used to get to the `required_alternate_scopes` attribute
        method: HTTP method name used as key for `required_alternate_scopes`

    Returns: dict of scopes
    """
    scopes = []
    if hasattr(view, 'required_alternate_scopes'):
        if method in view.required_alternate_scopes:
            for alt in view.required_alternate_scopes[method]:
                scopes.append({cls.openapi_security_scheme_name: alt})
    return scopes

openapi_security_scheme() classmethod

Source code in myapp/schemas.py
@classmethod
def openapi_security_scheme(cls):
    # N.B. Swagger UI still does not support OIDC, so always use OAuth2:
    scheme = {
        cls.openapi_security_scheme_name: {
            'type': 'oauth2',
            'description': 'OAuth 2.0 authentication',
        }
    }
    flows = {}
    if 'authorization_code' in settings.OAUTH2_CONFIG['grant_types_supported']:
        flows['authorizationCode'] = {
            'authorizationUrl': settings.OAUTH2_CONFIG['authorization_endpoint'],
            'tokenUrl': settings.OAUTH2_CONFIG['token_endpoint'],
            'refreshUrl': settings.OAUTH2_CONFIG['token_endpoint'],
            'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
    if 'implicit' in settings.OAUTH2_CONFIG['grant_types_supported']:
        flows['implicit'] = {
            'authorizationUrl': settings.OAUTH2_CONFIG['authorization_endpoint'],
            'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
    if 'client_credentials' in settings.OAUTH2_CONFIG['grant_types_supported']:
        flows['clientCredentials'] = {
            'tokenUrl': settings.OAUTH2_CONFIG['token_endpoint'],
            'refreshUrl': settings.OAUTH2_CONFIG['token_endpoint'],
            'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
    if 'password' in settings.OAUTH2_CONFIG['grant_types_supported']:
        flows['password'] = {
            'tokenUrl': settings.OAUTH2_CONFIG['token_endpoint'],
            'refreshUrl': settings.OAUTH2_CONFIG['token_endpoint'],
            'scopes': {s: s for s in settings.OAUTH2_CONFIG['scopes_supported']}}
    scheme[cls.openapi_security_scheme_name]['flows'] = flows

    # TODO: add JWT and SAML2 bearer

    # content = []
    # permission_classes can be a direct list of classes, or instances of Operands, etc.
    # for perm in cls.view.permission_classes:
    #     if (isinstance(perm(), TokenMatchesOASRequirements)
    #             or cls._drf_conditional_contains(perm(), TokenMatchesOASRequirements)
    #             or cls._rest_cond_contains(perm(), TokenMatchesOASRequirements)):
    #         alt_scopes = cls.view.required_alternate_scopes
    #         if method not in alt_scopes:
    #             continue
    #         for scopes in alt_scopes[method]:
    #             content.append({'oauth': scopes})
    #
    return scheme

SchemaGenerator (SchemaGenerator)

Source code in myapp/schemas.py
class SchemaGenerator(JSONAPISchemaGenerator):
    def get_schema(self, request, public):
        """
        Augments the automatically-generated `schema` with some additional
        documentation, servers. Also overrides not-yet-implemented in upstream DRF
        [security](https://github.com/encode/django-rest-framework/pull/7516).
        """
        schema = super().get_schema(request, public)
        schema['info'] = {
            'version': __version__,
            'title': __title__,
            'description':
                '![CUIT logo](https://cuit.columbia.edu/sites/default/files/logo/CUIT_Logo_286_web.jpg "CUIT logo")'
                '\n'
                '\n'
                '\n'
                'A sample API that uses courses as an example to demonstrate representing\n'
                '[JSON:API 1.0](http://jsonapi.org/format) in the OpenAPI 3.0 specification.\n'
                '\n'
                '\n'
                'See [https://columbia-it-django-jsonapi-training.readthedocs.io]'
                '(https://columbia-it-django-jsonapi-training.readthedocs.io)\n'
                'for more about this.\n'
                '\n'
                '\n' + __copyright__ + '\n',
            'contact': {
                'name': __author__
            },
            'license': {
                'name': __license__,
                'url': __license_url__
            }
        }
        schema['servers'] = [
            {'url': 'http://localhost:8000', 'description': 'local dev'},
            {'url': 'https://localhost', 'description': 'local docker'},
            {'url': 'https://ac45devapp01.cc.columbia.edu', 'description': 'demo'},
            {'url': '{serverURL}', 'description': 'provide your server URL',
             'variables': {'serverURL': {'default': 'http://localhost:8000'}}}
        ]

        # temporarily add securitySchemes until implemented upstream
        if 'securitySchemes' not in schema['components']:
            schema['components']['securitySchemes'] = {
                'basicAuth': {
                    'type': 'http',
                    'scheme': 'basic',
                    'description': 'basic authentication',
                },
                'sessionAuth': {
                    'type': 'apiKey',
                    'in': 'cookie',
                    'name': 'JSESSIONID',
                    'description': 'Session authentication'
                },
                'oauth-test': {
                    'type': 'oauth2',
                    'description': 'test OAuth2 service',
                    'flows': {
                        'authorizationCode': {
                            'authorizationUrl': 'https://oauth-test.cc.columbia.edu/as/authorization.oauth2',
                            'tokenUrl': 'https://oauth-test.cc.columbia.edu/as/token.oauth2',
                            'scopes': {
                                'auth-columbia': 'Columbia UNI login',
                                'create': 'create',
                                'read': 'read',
                                'update': 'update',
                                'delete': 'delete',
                                'openid': 'disclose your identity',
                                'profile': 'your user profile',
                                'email': 'your email address',
                                'https://api.columbia.edu/scope/group': 'groups you are a member of',
                                'demo-djt-sla-bronze':
                                    'permitted to access the django-jsonapi-training demo: 1 request per second',
                                'demo-djt-sla-update':
                                    'permitted to update the django-jsonapi-training resources'
                             }
                        }
                    }
                },
                'oidc-test': {
                    'type': 'openIdConnect',
                    'description': 'test OIDC service',
                    'openIdConnectUrl': 'https://oauth-test.cc.columbia.edu/.well-known/openid-configuration',
                },
                'oauth-dev': {
                    'type': 'oauth2',
                    'description': 'dev OAuth2 service',
                    'flows': {
                        'authorizationCode': {
                            'authorizationUrl': 'https://oauth-dev.cuit.columbia.edu:8443/as/authorization.oauth2',
                            'tokenUrl': 'https://oauth-dev.cuit.columbia.edu:8443/as/token.oauth2',
                            'scopes': {
                                'auth-columbia': 'Columbia UNI login',
                                'create': 'create',
                                'read': 'read',
                                'update': 'update',
                                'delete': 'delete',
                                'openid': 'disclose your identity',
                                'profile': 'your user profile',
                                'email': 'your email address',
                                'https://api.columbia.edu/scope/group': 'groups you are a member of',
                                'demo-djt-sla-bronze':
                                    'permitted to access the django-jsonapi-training demo: 1 request per second',
                                'demo-djt-sla-update':
                                    'permitted to update the django-jsonapi-training resources'
                             }
                        }
                    }
                },
                'oauth-local': {
                    'type': 'oauth2',
                    'description': 'local DOT OAuth2 service',
                    'flows': {
                        'authorizationCode': {
                            'authorizationUrl': 'http://localhost:8000/o/authorize/',
                            'tokenUrl': 'http://localhost:8000/o/token/',
                            'scopes': {
                                'auth-columbia': 'Columbia UNI login',
                                'create': 'create',
                                'read': 'read',
                                'update': 'update',
                                'delete': 'delete',
                                'openid': 'disclose your identity',
                                'profile': 'your user profile',
                                'email': 'your email address',
                                'https://api.columbia.edu/scope/group': 'groups you are a member of',
                                'demo-djt-sla-bronze':
                                    'permitted to access the django-jsonapi-training demo: 1 request per second',
                                'demo-djt-sla-update':
                                    'permitted to update the django-jsonapi-training resources'
                            }
                        }
                    }
                }
            }

        # temporarily add default security object at top-level
        if 'security' not in schema:
            schema['security'] = [
                {'basicAuth': []},
                {'sessionAuth': []},
                {'oauth-test': [['auth-columbia', 'openid', 'https://api.columbia.edu/scope/group']]},
                {'oauth-dev': [['auth-columbia', 'openid', 'https://api.columbia.edu/scope/group']]},
                {'oauth-local': [['auth-columbia', 'openid', 'https://api.columbia.edu/scope/group']]}
            ]

        return schema

get_schema(self, request, public)

Augments the automatically-generated schema with some additional documentation, servers. Also overrides not-yet-implemented in upstream DRF security.

Source code in myapp/schemas.py
def get_schema(self, request, public):
    """
    Augments the automatically-generated `schema` with some additional
    documentation, servers. Also overrides not-yet-implemented in upstream DRF
    [security](https://github.com/encode/django-rest-framework/pull/7516).
    """
    schema = super().get_schema(request, public)
    schema['info'] = {
        'version': __version__,
        'title': __title__,
        'description':
            '![CUIT logo](https://cuit.columbia.edu/sites/default/files/logo/CUIT_Logo_286_web.jpg "CUIT logo")'
            '\n'
            '\n'
            '\n'
            'A sample API that uses courses as an example to demonstrate representing\n'
            '[JSON:API 1.0](http://jsonapi.org/format) in the OpenAPI 3.0 specification.\n'
            '\n'
            '\n'
            'See [https://columbia-it-django-jsonapi-training.readthedocs.io]'
            '(https://columbia-it-django-jsonapi-training.readthedocs.io)\n'
            'for more about this.\n'
            '\n'
            '\n' + __copyright__ + '\n',
        'contact': {
            'name': __author__
        },
        'license': {
            'name': __license__,
            'url': __license_url__
        }
    }
    schema['servers'] = [
        {'url': 'http://localhost:8000', 'description': 'local dev'},
        {'url': 'https://localhost', 'description': 'local docker'},
        {'url': 'https://ac45devapp01.cc.columbia.edu', 'description': 'demo'},
        {'url': '{serverURL}', 'description': 'provide your server URL',
         'variables': {'serverURL': {'default': 'http://localhost:8000'}}}
    ]

    # temporarily add securitySchemes until implemented upstream
    if 'securitySchemes' not in schema['components']:
        schema['components']['securitySchemes'] = {
            'basicAuth': {
                'type': 'http',
                'scheme': 'basic',
                'description': 'basic authentication',
            },
            'sessionAuth': {
                'type': 'apiKey',
                'in': 'cookie',
                'name': 'JSESSIONID',
                'description': 'Session authentication'
            },
            'oauth-test': {
                'type': 'oauth2',
                'description': 'test OAuth2 service',
                'flows': {
                    'authorizationCode': {
                        'authorizationUrl': 'https://oauth-test.cc.columbia.edu/as/authorization.oauth2',
                        'tokenUrl': 'https://oauth-test.cc.columbia.edu/as/token.oauth2',
                        'scopes': {
                            'auth-columbia': 'Columbia UNI login',
                            'create': 'create',
                            'read': 'read',
                            'update': 'update',
                            'delete': 'delete',
                            'openid': 'disclose your identity',
                            'profile': 'your user profile',
                            'email': 'your email address',
                            'https://api.columbia.edu/scope/group': 'groups you are a member of',
                            'demo-djt-sla-bronze':
                                'permitted to access the django-jsonapi-training demo: 1 request per second',
                            'demo-djt-sla-update':
                                'permitted to update the django-jsonapi-training resources'
                         }
                    }
                }
            },
            'oidc-test': {
                'type': 'openIdConnect',
                'description': 'test OIDC service',
                'openIdConnectUrl': 'https://oauth-test.cc.columbia.edu/.well-known/openid-configuration',
            },
            'oauth-dev': {
                'type': 'oauth2',
                'description': 'dev OAuth2 service',
                'flows': {
                    'authorizationCode': {
                        'authorizationUrl': 'https://oauth-dev.cuit.columbia.edu:8443/as/authorization.oauth2',
                        'tokenUrl': 'https://oauth-dev.cuit.columbia.edu:8443/as/token.oauth2',
                        'scopes': {
                            'auth-columbia': 'Columbia UNI login',
                            'create': 'create',
                            'read': 'read',
                            'update': 'update',
                            'delete': 'delete',
                            'openid': 'disclose your identity',
                            'profile': 'your user profile',
                            'email': 'your email address',
                            'https://api.columbia.edu/scope/group': 'groups you are a member of',
                            'demo-djt-sla-bronze':
                                'permitted to access the django-jsonapi-training demo: 1 request per second',
                            'demo-djt-sla-update':
                                'permitted to update the django-jsonapi-training resources'
                         }
                    }
                }
            },
            'oauth-local': {
                'type': 'oauth2',
                'description': 'local DOT OAuth2 service',
                'flows': {
                    'authorizationCode': {
                        'authorizationUrl': 'http://localhost:8000/o/authorize/',
                        'tokenUrl': 'http://localhost:8000/o/token/',
                        'scopes': {
                            'auth-columbia': 'Columbia UNI login',
                            'create': 'create',
                            'read': 'read',
                            'update': 'update',
                            'delete': 'delete',
                            'openid': 'disclose your identity',
                            'profile': 'your user profile',
                            'email': 'your email address',
                            'https://api.columbia.edu/scope/group': 'groups you are a member of',
                            'demo-djt-sla-bronze':
                                'permitted to access the django-jsonapi-training demo: 1 request per second',
                            'demo-djt-sla-update':
                                'permitted to update the django-jsonapi-training resources'
                        }
                    }
                }
            }
        }

    # temporarily add default security object at top-level
    if 'security' not in schema:
        schema['security'] = [
            {'basicAuth': []},
            {'sessionAuth': []},
            {'oauth-test': [['auth-columbia', 'openid', 'https://api.columbia.edu/scope/group']]},
            {'oauth-dev': [['auth-columbia', 'openid', 'https://api.columbia.edu/scope/group']]},
            {'oauth-local': [['auth-columbia', 'openid', 'https://api.columbia.edu/scope/group']]}
        ]

    return schema