コード例 #1
0
class IBaseUser(IEmail):
    """"Membrane based based user Class."""
    first_name = schema.TextLine(title=_('First Name'), )
    last_name = schema.TextLine(title=_('Last name'))
    mfa = schema.Choice(title=_('Multi-factor authentication (MFA)'),
                        required=False,
                        vocabulary='mfa_types')
    oidc_enabled = schema.Bool(
        title=_('Is OpenID Connect enabled'),
        required=False,
    )
コード例 #2
0
class ITokenCodeBase(model.Schema):
    """OID Map:
    :user_id = user
    :client_id = client"""
    user_id = ps.TextLine(title=_('User ID'), required=True)
    client_id = ps.TextLine(title=_('Client ID'), required=True)
    scope = ps.List(title=_('List of scopes'),
                    required=False,
                    value_type=ps.TextLine())
    expire_at = ps.Datetime(title=_('Token Expire Date'), required=True)

    @invariant
    def validate_existance(self, data):
        """Validate if User and Application Is exists"""
        return Invalid('')
コード例 #3
0
ファイル: behaviors.py プロジェクト: nazrulworld/plone.oidc
class IOidConnectClaims(IProvideOidConnectClaims):
    """Add password fields"""

    # Putting this in a separate fieldset for the moment:
    model.fieldset(
        'claims',
        label=_(u'OpenID Connect Claims'),
        fields=['phone_number', 'address']
    )

    directives.omitted(
        'sub',
        'name',
        'given_name',
        'family_name',
        'preferred_username',
        'email_verified',
        'phone_number_verified',
        'updated_at'
    )
コード例 #4
0
class IJWTBearerToken(ITokenCodeBase):
    """ """
    access_token = ps.TextLine(title=_('Access token'))
    refresh_token = ps.TextLine(title=_('Refresh token'))
    id_token = ps.JSONField(title=_('ID Token'))
コード例 #5
0
ファイル: behaviors.py プロジェクト: nazrulworld/plone.oidc
class IProvideOidConnectClaims(model.Schema):
    """Add OpenID Connect Claims fields.
    http://openid.net/specs/openid-connect-core-1_0.html#Claims
    """

    sub = ps.TextLine(
        title=_('Subject'),
        required=False,
    )

    name = ps.TextLine(
        title=_('Full Name'),
        description=_(
            "End-User's full name in displayable form including all name parts, "
            'possibly including titles and suffixes, '
            "ordered according to the End-User's locale and preferences."
        ),
        required=False,
    )
    given_name = ps.TextLine(
        title=_('First Name'),
        description=_(
            'Given name(s) or first name(s) of the End-User. Note that in some cultures, '
            'people can have multiple given names; all can be present, with the names being '
            'separated by space characters'
        ),
        required=False
    )
    family_name = ps.TextLine(
        title=_('Last Name'),
        description=_(
            'Surname(s) or last name(s) of the End-User. Note that in some cultures, '
            'people can have multiple family names or no family name; all can be present, '
            'with the names being separated by space characters.'
        ),
        required=False
    )
    middle_name = ps.TextLine(
        title=_('Middle Name'),
        description=_(
            'Middle name(s) of the End-User. Note that in some cultures, '
            'people can have multiple middle names; all can be present, '
            'with the names being separated by space characters. '
            'Also note that in some cultures, middle names are not used.'
        ),
        required=False
    )
    nickname = ps.TextLine(
        title=_('Nickname'),
        description=_(
            'Casual name of the End-User that may or may not be the same as the given_name. '
            'For instance, a nickname value of Mike might be returned alongside a given_name '
            'value of Michael.'
        ),
        required=False
    )
    preferred_username = ps.TextLine(
        title=_('Username'),
        description=_(
            'Given name(s) or first name(s) of the End-User. Note that in some cultures, '
            'people can have multiple given names; all can be present, with the names being '
            'separated by space characters'
        ),
        required=False
    )
    profile = ps.URI(
        title=_('Profile URL'),
        description=_(
            "URL of the End-User\'s profile page. "
            'The contents of this Web page SHOULD be about the End-User.'
        ),
        required=False,
    )
    picture = NamedBlobImage(
        title=_('profile picture'),
        description=_(
            "URL of the End-User\'s profile picture. "
            'This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), '
            'rather than to a Web page containing an image. '
            'Note that this URL SHOULD specifically reference a profile photo of the End-User '
            'suitable for displaying when describing the End-User, '
            'rather than an arbitrary photo taken by the End-User.'
        ),
        required=False
    )

    website = ps.URI(
        title=_('Website URL'),
        description=_(
            "URL of the End-User\'s Web page or blog. "
            'This Web page SHOULD contain information published by '
            'the End-User or an organization that the End-User is affiliated with.'
        ),
        required=False
    )
    email_verified = ps.Bool(
        title=_('Id Email Verified'),
        description=_(
            'controlled by the End-User at the time the verification was performed. '
            'The means by which an e-mail address is verified is context-specific, '
            'and dependent upon the trust framework or contractual agreements within '
            'which the parties are operating.'
        ),
        required=False
    )
    gender = ps.Choice(
        title=_('Gender'),
        description=_(
            "End-User's gender. Values defined by this specification are female and male. "
            'Other values MAY be used when neither of the defined values are applicable.'
        ),
        vocabulary='gender_options'
    )

    birthdate = ps.Date(
        title=_('Birth Date'),
        description=_(
            str("End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] ").decode('utf-8') +
            'YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. '
            'To represent only the year, YYYY format is allowed. '
            "Note that depending on the underlying platform's date related function, "
            'providing just year can result in varying month and day, '
            'so the implementers need to take this factor into account to '
            'correctly process the dates.'
        ),
        required=True
    )

    zoneinfo = ps.Choice(
        title=_('Time Zone'),
        description=_(
            'String from zoneinfo [zoneinfo] time zone database representing the '
            'End-User\'s time zone. For example, Europe/Paris or America/Los_Angeles.'
        ),
        vocabulary='plone.app.vocabularies.CommonTimezones',
        required=False
    )

    locale = ps.TextLine(
        title=_('Language'),
        description=_(
            str('End-User\'s locale, represented as a BCP47 [RFC5646] language tag. '
                'This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in '
                'lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, ').decode('utf-8') +
            'separated by a dash. For example, en-US or fr-CA. As a compatibility note, '
            'some implementations have used an underscore as the separator rather than a dash, '
            'for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.'
        ),
        required=False
    )
    phone_number = ps.TextLine(
        title=_('Phone Number'),
        description=_(
            'End-User\'s preferred telephone number. '
            'E.164 [E.164] is RECOMMENDED as the format of this Claim, '
            'for example, +1 (425) 555-1212 or +56 (2) 687 2400. '
            'If the phone number contains an extension, it is RECOMMENDED '
            'that the extension be represented using the RFC 3966 [RFC3966] '
            'extension syntax, for example, +1 (604) 555-1234;ext=5678.'
        ),
        required=False
    )

    phone_number_verified = ps.Bool(
        title=_('Id Phone Number Verified'),
        description=_(
            'True if the End-User\'s phone number has been verified; otherwise false. '
            'When this Claim Value is true, this means that the OP took affirmative '
            'steps to ensure that this phone number was controlled by the '
            'End-User at the time the verification was performed. The means by which a '
            'phone number is verified is context-specific, and dependent upon the '
            'trust framework or contractual agreements within which the parties are operating. '
            'When true, the phone_number Claim MUST be in E.164 format and any extensions MUST '
            'be represented in RFC 3966 format'
        ),
        required=False
    )

    address = ps.JSONField(
        title=_('Address'),
        description=_(
            'End-User\'s preferred postal address. '
            'The value of the address member is a JSON [RFC4627] structure containing '
            'some or all of the members defined in Section 5.1.1.'
        ),
        required=False
    )
    updated_at = ps.Datetime(
        title=_('last update time'),
        description=_(
            'number Time the End-User\'s information was last updated. '
            'Its value is a JSON number representing the number of seconds '
            'from 1970-01-01T0:0:0Z as measured in UTC until the date/time.'
        ),
        required=False
    )
コード例 #6
0
ファイル: behaviors.py プロジェクト: nazrulworld/plone.oidc
 def sub(self, value):
     # When editing, the password field is empty in the browser; do
     # not do anything then.
     raise ValueError(
         _('Assigning value for sub is not allowed, it should derive from ID')
     )
コード例 #7
0
ファイル: client.py プロジェクト: nazrulworld/plone.oidc
class IOAuth2Client(model.Schema):
    """Meta Info:
    http://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata"""

    client_name = ps.TextLine(
        title=_('Client Name'),
        description=_(
            'Name of the Client to be presented to the End-User. '
            'If desired, representation of this Claim in different '
            'languages and scripts is represented as described in Section 2.1.'
        ),
        required=True)
    redirect_uris = ps.List(
        title=_('Redirect URIs'),
        description=_(
            'Array of Redirection URI values used by the Client. '
            'One of these registered Redirection URI values MUST '
            'exactly match the redirect_uri parameter value used in each '
            'Authorization Request, with the matching performed as described in '
            'Section 6.2.1 of [RFC3986] (Simple String Comparison).'),
        value_type=ps.URI(),
        required=True)
    response_types = ps.List(
        title=_('Response Type'),
        description=
        _('JSON array containing a list of the OAuth 2.0 response_type values '
          'that the Client is declaring that it will restrict itself to using. '
          'If omitted, the default is that the Client will use only the code Response Type.'
          ),
        value_type=ps.Choice(vocabulary='oauth2_client_response_type_options'))

    grant_types = ps.List(
        title=_('Grant Types'),
        description=_(
            'JSON array containing a list of the OAuth 2.0 Grant Types that the '
            'Client is declaring that it will restrict itself to using. '),
        value_type=ps.Choice(vocabulary='oauth2_client_grant_type_options'),
        required=False)

    application_type = ps.Choice(
        title=_('Application Type'),
        description=
        _('Kind of the application. The default, if omitted, is web. '
          'The defined values are native or web. Web Clients using the OAuth Implicit '
          'Grant Type MUST only register URLs using the https scheme as redirect_uris; '
          'they MUST NOT use localhost as the hostname. Native Clients MUST only register '
          'redirect_uris using custom URI schemes or URLs using the http: scheme with '
          'localhost as the hostname. Authorization Servers MAY place additional constraints '
          'on Native Clients. Authorization Servers MAY reject Redirection URI values using '
          'the http scheme, other than the localhost case for Native Clients. '
          'The Authorization Server MUST verify that all the registered redirect_uris '
          'conform to these constraints. This prevents sharing a Client ID across different '
          'types of Clients'),
        vocabulary='oauth2_app_type_options',
        default='web')

    contacts = ps.List(
        title=_('Contact E-mails'),
        description=_(
            'Array of e-mail addresses of people responsible for this Client. '
            'This might be used by some providers to enable a Web user '
            'interface to modify the Client information.'),
        value_type=ps.TextLine(),
        required=False)

    logo_uri = ps.URI(
        title=_('Logo URI'),
        description=_(
            'URL that references a logo for the Client application. '
            'If present, the server SHOULD display this image to the '
            'End-User during approval. The value of this field MUST point '
            'to a valid image file. If desired, representation of this '
            'Claim in different languages and scripts is represented as '
            'described in Section 2.1.'),
        required=False)

    client_uri = ps.URI(
        title=_('Client URI'),
        description=
        _('URL of the home page of the Client. The value of this field MUST point to a '
          'valid Web page. If present, the server SHOULD display this URL to '
          'the End-User in a followable fashion. If desired, representation of this '
          'Claim in different languages and scripts is represented as described in Section 2.1.'
          ),
        required=False)

    policy_uri = ps.URI(
        title=_('Policy URI'),
        description=
        _('URL that the Relying Party Client provides to the End-User to '
          'read about the how the profile data will be used. '
          'The value of this field MUST point to a valid web page. '
          'The OpenID Provider SHOULD display this URL to the End-User if it is given. '
          'If desired, representation of this Claim in different languages and scripts '
          'is represented as described in Section 2.1.'),
        required=False)

    tos_uri = ps.URI(
        title=_('Terms of service URI'),
        description=
        _('URL that the Relying Party Client provides to the End-User to read about '
          'the Relying Party\'s terms of service. The value of this field MUST point '
          'to a valid web page. The OpenID Provider SHOULD display this URL to '
          'the End-User if it is given. If desired, representation of this Claim '
          'in different languages and scripts is represented as described in Section 2.1.'
          ),
        required=False)

    jwks_uri = ps.URI(
        title=_('JWKS URI'),
        description=
        _('URL for the Client\'s JSON Web Key Set [JWK] document. If the Client signs requests '
          'to the Server, it contains the signing key(s) the Server uses to validate signatures '
          'from the Client. The JWK Set MAY also contain the Client\'s encryption keys(s), '
          'which are used by the Server to encrypt responses to the Client. When both signing '
          'and encryption keys are made available, a use (Key Use) parameter value is '
          'REQUIRED for all keys in the referenced JWK Set to indicate each key\'s intended usage. '
          'Although some algorithms allow the same key to be used for both signatures and '
          'encryption, doing so is NOT RECOMMENDED, as it is less secure. '
          'The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. '
          'When used, the bare key values MUST still be present and MUST match those in '
          'the certificate.'),
        required=False)
    # jwks: https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41#section-3
    jwks = ps.Dict(
        title=_('JSON Web Key Set'),
        description=
        _('JSON Object. JSON Web Key Set [JWK] document, passed by value. '
          'The semantics of the jwks parameter are the same as the jwks_uri parameter, '
          'other than that the JWK Set is passed by value, rather than by reference. '
          'This parameter is intended only to be used by Clients that, for some reason, '
          'are unable to use the jwks_uri parameter, for instance, by native applications '
          'that might not have a location to host the contents of the JWK Set. '
          'If a Client can use jwks_uri, it MUST NOT use jwks. One significant downside of '
          'jwks is that it does not enable key rotation (which jwks_uri does, as described '
          'in Section 10 of OpenID Connect Core 1.0 [OpenID.Core]). '
          'The jwks_uri and jwks parameters MUST NOT be used together.'),
        required=False)

    sector_identifier_uri = ps.URI(
        title=_('Sector Identifier URI'),
        description=
        _('URL using the https scheme to be used in calculating Pseudonymous Identifiers '
          'by the OP. The URL references a file with a single JSON array of redirect_uri values. '
          'Please see Section 5. Providers that use pairwise sub (subject) values SHOULD '
          'utilize the sector_identifier_uri value provided in the Subject Identifier calculation '
          'for pairwise identifiers.'))

    subject_type = ps.Choice(
        title=_('Subject Type'),
        description=
        _('subject_type requested for responses to this Client. '
          'The subject_types_supported Discovery parameter contains a list of '
          'the supported subject_type values for this server. Valid types include '
          'pairwise and public.'),
        values=('pairwise', 'public'),
        required=False)

    id_token_signed_response_alg = ps.Choice(
        title=_('ID Token Response algorithms'),
        description=
        _('JWS alg algorithm [JWA] REQUIRED for signing the ID Token issued to this Client.'
          'The value none MUST NOT be used as the ID Token alg value unless the '
          'Client uses only Response Types that return no ID Token from the '
          'Authorization Endpoint (such as when only using the Authorization Code Flow). '
          'The default, if omitted, is RS256. The public key for validating the '
          'signature is provided by retrieving the JWK Set referenced by the '
          'jwks_uri element from OpenID Connect Discovery 1.0 [OpenID.Discovery].'
          ),
        vocabulary='oauth2_client_jwt_algs_options',
        required=False)

    id_token_encrypted_response_alg = ps.Choice(
        title=_('ID Token encrypted response algorithm'),
        description=
        _('JWE alg algorithm [JWA] REQUIRED for encrypting the ID Token issued to this Client. '
          'If this is requested, the response will be signed then encrypted, with the result '
          'being a Nested JWT, as defined in [JWT]. The default, if omitted, is that no '
          'encryption is performed.'),
        vocabulary='oauth2_client_jwt_algs_options',
        required=False)

    id_token_encrypted_response_enc = ps.Choice(
        title=_('ID Token encrypted response enc'),
        description=
        _('JWE enc algorithm [JWA] REQUIRED for encrypting the ID Token issued to this Client. '
          'If id_token_encrypted_response_alg is specified, the default for this value is '
          'A128CBC-HS256. When id_token_encrypted_response_enc is included, '
          'id_token_encrypted_response_alg MUST also be provided.'),
        vocabulary='oauth2_client_jwt_enc_options',
        required=False)

    userinfo_signed_response_alg = ps.Choice(
        title=_('userinfo_signed_response_alg'),
        description=
        _('JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. '
          'If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. '
          'The default, if omitted, is for the UserInfo Response to return the Claims as a '
          'UTF-8 encoded JSON object using the application/json content-type.'
          ),
        vocabulary='oauth2_client_jwt_algs_options',
        required=False)

    userinfo_encrypted_response_alg = ps.Choice(
        title=_('userinfo encrypted response algorithm'),
        description=
        _('JWE [JWE] alg algorithm [JWA] REQUIRED for encrypting UserInfo '
          'Responses. If both signing and encryption are requested, the response will '
          'be signed then encrypted, with the result being a Nested JWT, as defined in '
          '[JWT]. The default, if omitted, is that no encryption is performed.'
          ),
        vocabulary='oauth2_client_jwt_algs_options',
        required=False)

    request_object_signing_alg = ps.Choice(
        title=_('request_object_signing_alg'),
        description=
        _('JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent '
          'to the OP. All Request Objects from this Client MUST be rejected, '
          'if not signed with this algorithm. Request Objects are described in Section 6.1 of '
          'OpenID Connect Core 1.0 [OpenID.Core]. This algorithm MUST be used both when the '
          'Request Object is passed by value (using the request parameter) and when '
          'it is passed by reference (using the request_uri parameter). '
          'Servers SHOULD support RS256. The value none MAY be used. The default, if omitted, '
          'is that any algorithm supported by the OP and the RP MAY be used.'),
        vocabulary='oauth2_client_jwt_algs_options',
        required=False)

    request_object_encryption_alg = ps.Choice(
        title=_('request_object_encryption_alg'),
        description=
        _('JWE [JWE] alg algorithm [JWA] the RP is declaring that it may use for '
          'encrypting Request Objects sent to the OP. This parameter SHOULD be included when '
          'symmetric encryption will be used, since this signals to the OP that a client_secret '
          'value needs to be returned from which the symmetric key will be derived, '
          'that might not otherwise be returned. The RP MAY still use other supported encryption '
          'algorithms or send unencrypted Request Objects, even when this parameter is present. '
          'If both signing and encryption are requested, the Request Object will be signed then '
          'encrypted, with the result being a Nested JWT, as defined in [JWT]. '
          'The default, if omitted, is that the RP is not declaring whether it might encrypt any '
          'Request Objects.'),
        vocabulary='oauth2_client_req_obj_algs_options',
        required=False)
    request_object_encryption_enc = ps.Choice(
        title=_('request_object_encryption_enc'),
        description=
        _('WE enc algorithm [JWA] the RP is declaring that it may use for encrypting '
          'Request Objects sent to the OP. If request_object_encryption_alg is specified, '
          'the default for this value is A128CBC-HS256. When request_object_encryption_enc '
          'is included, request_object_encryption_alg MUST also be provided.'),
        vocabulary='oauth2_client_jwt_enc_options',
        required=False)

    token_endpoint_auth_method = ps.Choice(
        title=_('token_endpoint_auth_method'),
        description=
        _('Requested Client Authentication method for the Token Endpoint. '
          'The options are client_secret_post, client_secret_basic, client_secret_jwt, '
          'private_key_jwt, and none, as described in Section 9 of '
          'OpenID Connect Core 1.0 [OpenID.Core]. Other authentication methods MAY be '
          'defined by extensions. If omitted, the default is client_secret_basic -- '
          'the HTTP Basic Authentication Scheme specified in Section 2.3.1 of '
          'OAuth 2.0 [RFC6749].'),
        vocabulary='oauth2_client_token_endpoint_auth_method_options',
        required=False)
    token_endpoint_auth_signing_alg = ps.Choice(
        title=_('token_endpoint_auth_signing_alg'),
        description=
        _('JWS [JWS] alg algorithm [JWA] that MUST be used for signing the JWT [JWT] '
          'used to authenticate the Client at the Token Endpoint for the private_key_jwt '
          'and client_secret_jwt authentication methods. All Token Requests using these '
          'authentication methods from this Client MUST be rejected, if the JWT is not '
          'signed with this algorithm. Servers SHOULD support RS256. '
          'The value none MUST NOT be used. The default, if omitted, is that any '
          'algorithm supported by the OP and the RP MAY be used.'),
        vocabulary='oauth2_client_jwt_algs_options',
        required=False)

    default_max_age = ps.Decimal(
        title=_('Default Maximum Age'),
        description=
        _('Default Maximum Authentication Age. Specifies that the End-User MUST be '
          'actively authenticated if the End-User was authenticated longer ago than '
          'the specified number of seconds. The max_age request parameter overrides this '
          'default value. If omitted, no default Maximum Authentication Age is specified.'
          ),
        required=False)

    require_auth_time = ps.Bool(
        title=_('Is require auth time'),
        description=
        _('Boolean value specifying whether the auth_time Claim in the ID Token is REQUIRED. '
          'It is REQUIRED when the value is true. (If this is false, the auth_time Claim '
          'can still be dynamically requested as an individual Claim for the ID Token using '
          'the claims request parameter described in Section 5.5.1 of OpenID Connect Core 1.0 '
          '[OpenID.Core].) If omitted, the default value is false.'),
        required=False)
    default_acr_values = ps.TextLine(
        title=_('default_acr_values'),
        description=
        _('Default requested Authentication Context Class Reference values. '
          'Array of strings that specifies the default acr values that the OP '
          'is being requested to use for processing requests from this Client, '
          'with the values appearing in order of preference. The Authentication Context '
          'Class satisfied by the authentication performed is returned as the acr '
          'Claim Value in the issued ID Token. The acr Claim is requested as a Voluntary '
          'Claim by this parameter. The acr_values_supported discovery element contains a '
          'list of the supported acr values supported by this server. Values specified in '
          'the acr_values request parameter or an individual acr Claim request override '
          'these default values.'),
        required=False)

    initiate_login_uri = ps.URI(
        title=_('initiate_login_uri'),
        description=
        _('URI using the https scheme that a third party can use to initiate a login by the RP, '
          'as specified in Section 4 of OpenID Connect Core 1.0 [OpenID.Core]. '
          'The URI MUST accept requests via both GET and POST. The Client MUST understand '
          'the login_hint and iss parameters and SHOULD support the target_link_uri parameter.'
          ),
        required=False)
    request_uris = ps.List(
        title=_('request_uris'),
        description=
        _('Array of request_uri values that are pre-registered by the RP for use at the OP. '
          'Servers MAY cache the contents of the files referenced by these URIs and not '
          'retrieve them at the time they are used in a request. OPs can require that '
          'request_uri values used be pre-registered with the require_request_uri_registration '
          'discovery parameter. If the contents of the request file could ever change, these URI '
          'values SHOULD include the base64url encoded SHA-256 hash value of the file contents '
          'referenced by the URI as the value of the URI fragment. If the fragment value used for '
          'a URI changes, that signals the server that its cached value for that URI with the old '
          'fragment value is no longer valid.'),
        value_type=ps.URI(),
        required=False)
    # http://openid.net/specs/openid-connect-session-1_0.html#OPMetadata
    post_logout_redirect_uri = ps.List(
        title=_('post_logout_redirect_uri'),
        description=
        _('Array of URLs supplied by the RP to which it MAY request that the End-User\'s '
          'User Agent be redirected using the post_logout_redirect_uri parameter after a '
          'logout has been performed.'),
        value_type=ps.URI(),
        required=False)
    # https://www.rfc-editor.org/rfc/rfc7591.txt
    scope = ps.TextLine(
        title=_('Scope'),
        description=_(
            'String containing a space-separated list of scope values (as'
            'described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client'
            'can use when requesting access tokens.  The semantics of values in'
            'this list are service specific.  If omitted, an authorization'
            'server MAY register a client with a default set of scopes.'),
        required=False)
    software_id = ps.TextLine(
        title=_('Software ID'),
        description=_(
            'A unique identifier string (e.g., a Universally Unique Identifier'
            '(UUID)) assigned by the client developer or software publisher'
            'used by registration endpoints to identify the client software to'
            'be dynamically registered.'),
        required=False)
    client_id = ps.TextLine(
        title=_('Client ID'),
        description=_(
            'OAuth 2.0 client identifier string.  It SHOULD NOT be'
            'currently valid for any other registered client, though an'
            'authorization server MAY issue the same client identifier to'
            'multiple instances of a registered client at its discretion.'),
        required=True)
    client_secret = ps.TextLine(
        title=_('Client Secret'),
        description=_(
            'OAuth 2.0 client secret string.  If issued, this MUST'
            'be unique for each "client_id" and SHOULD be unique for multiple'
            'instances of a client using the same "client_id".  This value is'
            'used by confidential clients to authenticate to the token'
            'endpoint, as described in OAuth 2.0 [RFC6749], Section 2.3.1.'),
        required=False)

    client_type = ps.Choice(
        title=_('Client Type'),
        description=_('Type clients, i.e public or secret'),
        vocabulary='oauth2_client_type_options',
        required=False)

    client_id_issued_at = ps.Datetime(
        title=_('Client ID Issued at'),
        description=_(
            'Time at which the client identifier was issued.  The'
            'time is represented as the number of seconds from'
            '1970-01-01T00:00:00Z as measured in UTC until the date/time of'
            'issuance.'))

    client_secret_expires_at = ps.Datetime(
        title=_('Client Secret Expire at'),
        description=_(
            'REQUIRED if "client_secret" is issued.  Time at which the client'
            'secret will expire or 0 if it will not expire.  The time is'
            'represented as the number of seconds from 1970-01-01T00:00:00Z as'
            'measured in UTC until the date/time of expiration.'),
        required=False)

    domain = ps.TextLine(title=_('FQ Domain Name'),
                         description=_(''),
                         required=False)