mirror of
https://github.com/moparisthebest/xeps
synced 2024-12-22 07:38:52 -05:00
418 lines
19 KiB
XML
418 lines
19 KiB
XML
|
<?xml version='1.0' encoding='UTF-8'?>
|
|||
|
<!DOCTYPE xep SYSTEM 'xep.dtd' [
|
|||
|
<!ENTITY % ents SYSTEM 'xep.ent'>
|
|||
|
%ents;
|
|||
|
]>
|
|||
|
<?xml-stylesheet type='text/xsl' href='xep.xsl'?>
|
|||
|
<xep>
|
|||
|
<header>
|
|||
|
<title>Authorization Tokens</title>
|
|||
|
<abstract>This document defines an XMPP protocol extension for issuing authentication tokens to client applications and provides methods for managing сlient connections.</abstract>
|
|||
|
&LEGALNOTICE;
|
|||
|
<number>xxxx</number>
|
|||
|
<status>ProtoXEP</status>
|
|||
|
<type>Standards Track</type>
|
|||
|
<sig>Standards</sig>
|
|||
|
<approver>Council</approver>
|
|||
|
<dependencies>
|
|||
|
<spec>XMPP Core</spec>
|
|||
|
<spec>XEP-0001</spec>
|
|||
|
</dependencies>
|
|||
|
<supersedes/>
|
|||
|
<supersededby/>
|
|||
|
<shortname>xabber-tokens</shortname>
|
|||
|
<author>
|
|||
|
<firstname>Andrey</firstname>
|
|||
|
<surname>Gagarin</surname>
|
|||
|
<email>andrey.gagarin@redsolution.com</email>
|
|||
|
<jid>andrey.gagarin@redsolution.com</jid>
|
|||
|
</author>
|
|||
|
<author>
|
|||
|
<firstname>Andrew</firstname>
|
|||
|
<surname>Nenakhov</surname>
|
|||
|
<email>andrew.nenakhov@redsolution.com</email>
|
|||
|
<jid>andrew.nenakhov@redsolution.com</jid>
|
|||
|
</author>
|
|||
|
<revision>
|
|||
|
<version>0.0.1</version>
|
|||
|
<date>2019-09-11</date>
|
|||
|
<initials>ag/an</initials>
|
|||
|
<remark><p>First draft.</p></remark>
|
|||
|
</revision>
|
|||
|
</header>
|
|||
|
<section1 topic='Introduction' anchor='intro'>
|
|||
|
<p>When an XMPP client is negotiating a stream with an XMPP server, it typically needs to perform authentication and authorization. Typically stream negotiation requires providing a password on each connection attempt. Naturally this means that an XMPP client needs to store password: reconnections are very frequent on some types of clients, asking a user to provide a password on each reconnect would result in a very bad user experience.</p>
|
|||
|
<p>This results in increased security threats associated with storing account password on physical device: password can be extracted from this device by whoever gains access to it. Also, a user can't revoke access to clients without changing password. More, password change won't help to immediately revoke access from a device with established connection.</p>
|
|||
|
<p>This document describes a method address mentioned issues and provide more security for users. The idea is to use XMPP-based tokens, which allow to control client sessions.</p>
|
|||
|
</section1>
|
|||
|
<section1 topic='Requirements' anchor='reqs'>
|
|||
|
<ol>
|
|||
|
<li>It is RECOMMENDED for the client to transfer information about connected device: type of client, version of the operating system.</li>
|
|||
|
<li>Server MUST show latest IP address used with the token</li>
|
|||
|
</ol>
|
|||
|
</section1>
|
|||
|
<section1 topic='Obtaining a token' anchor='obtaining_a_token'>
|
|||
|
<section2 topic='Issuing a token' anchor='issuing_a_token'>
|
|||
|
<p>The following example illustrates the XML structure to be sent when issuing new token for client.</p>
|
|||
|
<example caption='The client sends a request with information about his operating system and the client version to the server to issue new token'><![CDATA[
|
|||
|
<iq type='set' from='juliet@capulet.lit/chamber' to='capulet.it' id='1'>
|
|||
|
<issue xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<client>xabber-web</client>
|
|||
|
<device>MacOS 10.14</device>
|
|||
|
</issue>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='The server in response sends a new token to the the client'><![CDATA[
|
|||
|
<iq type='result' from='capulet.it' to='juliet@capulet.it/balcony' id='1'>
|
|||
|
<x xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<token>VkpTYqfpPcLpwciTRtgHaV7HLBC9O9kY</token>
|
|||
|
<expire>1536322632</expire>
|
|||
|
<token-uid>49975a48609793c5c93f5e9eab264f6706f04164</token-uid>
|
|||
|
</x>
|
|||
|
</iq>]]></example>
|
|||
|
</section2>
|
|||
|
<section2 topic='Issuing a token with a specified lifetime' anchor='issuing_a_token_with_lifetime'>
|
|||
|
<example caption='If the client wants to explicitly indicate the lifetime of the token, it MAY add the expire tag with expiration time in seconds'><![CDATA[
|
|||
|
<iq type='set' from='juliet@capulet.lit/chamber' to='capulet.it' id='2'>
|
|||
|
<issue xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<client>xabber-web</client>
|
|||
|
<device>iMac Pro MacOS 10.14</device>
|
|||
|
<expire>3600</expire>
|
|||
|
</issue>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='The server in response sends a new token to the the client'><![CDATA[
|
|||
|
<iq type='result' from='capulet.it' to='juliet@capulet.it/balcony' id='2'>
|
|||
|
<x xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<token>VkpTYqfpPcLpwciTRtgHaV7HLBC9O9kY</token>
|
|||
|
<expire>1536321232</expire>
|
|||
|
<token-uid>49975a48609793c5c93f5e9eab264f6706f04164</token-uid>
|
|||
|
</x>
|
|||
|
</iq>]]></example>
|
|||
|
</section2>
|
|||
|
<section2 topic='Issuing a new token during authentication before bind' anchor='issuing_a_token_before_bind'>
|
|||
|
<p>During authentication process the client can receive a token before bind.</p>
|
|||
|
<example caption='The client establishes a connection to the server'><![CDATA[
|
|||
|
<?xml version='1.0'?>
|
|||
|
<stream:stream xmlns="jabber:client" to="capulet.it" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xml:lang="ru">]]></example>
|
|||
|
<p>The server accepts the connection and sends a list of supported stream features. Feature x-token is also on the list:</p>
|
|||
|
<example><![CDATA[
|
|||
|
<?xml version='1.0'?>
|
|||
|
<stream:stream xmlns="jabber:client" to="capulet.it" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xml:lang="ru">
|
|||
|
|
|||
|
<stream:features>
|
|||
|
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
|
|||
|
...
|
|||
|
<x-token xmlns='https://xabber.com/protocol/auth-tokens'/>
|
|||
|
</stream:features>]]></example>
|
|||
|
<example caption='After passing all authentication steps, the client makes a standard request to issue a token'><![CDATA[
|
|||
|
<iq to='capulet.it' type='set' id='2'>
|
|||
|
<issue xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<client>xabber-web</client>
|
|||
|
<device>MacOS 10.14</device>
|
|||
|
</issue>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='The server responds with a new client token'><![CDATA[
|
|||
|
<iq from='capulet.it' type='result' id='2'>
|
|||
|
<x xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<token>VkpTYqfpPcLpwciTRtgHaV7HLBC9O9kY</token>
|
|||
|
<expire>1536322632</expire>
|
|||
|
<token-uid>49975a48609793c5c93f5e9eab264f6706f04164</token-uid>
|
|||
|
</x>
|
|||
|
</iq>]]></example>
|
|||
|
</section2>
|
|||
|
</section1>
|
|||
|
<section1 topic='Authentication' anchor='authentication'>
|
|||
|
<example caption='The client establishes a connection with the server'><![CDATA[
|
|||
|
<?xml version='1.0'?>
|
|||
|
<stream:stream xmlns="jabber:client" to="capulet.it" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xml:lang="ru" >]]></example>
|
|||
|
<example caption='The server accepts this connection and sends a list of supported protocols for authentication'><![CDATA[
|
|||
|
<?xml version='1.0'?>
|
|||
|
<stream:stream xmlns="jabber:client" to="capulet.it" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xml:lang="ru" >
|
|||
|
|
|||
|
<stream:features>
|
|||
|
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
|
|||
|
...
|
|||
|
<mechanism>X-TOKEN</mechanism>
|
|||
|
...
|
|||
|
</mechanisms>
|
|||
|
</stream:features>]]></example>
|
|||
|
<example caption='The client sends the next element for authentication:'><![CDATA[
|
|||
|
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-TOKEN">
|
|||
|
base64("\0" + user_name + "\0" + auth_token)
|
|||
|
</auth>]]></example>
|
|||
|
<p>The content in the auth element should be base64 encoding of a string containing a null byte, followed by username, another null byte and the string representation of the user authentication token. This is similar to authentication with a password using the PLAIN mechanism, except the token is added instead of password.</p>
|
|||
|
<example caption='The response is standard for SASL XMPP authentication. For example, on success, the server will reply with:'><![CDATA[
|
|||
|
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>]]></example>
|
|||
|
</section1>
|
|||
|
<section1 topic='Token management' anchor='token_management'>
|
|||
|
<section2 topic='List of tokens' anchor='token_list'>
|
|||
|
<example caption='To get a list of all existing tokens, the client sends a request to the server'><![CDATA[
|
|||
|
<iq type='get' from='juliet@capulet.it/balcony' to='capulet.it' id='3'>
|
|||
|
<query xmlns='https://xabber.com/protocol/auth-tokens#items'/>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='The server responds to the client with a list of tokens with unique identifiers'><![CDATA[
|
|||
|
<iq type='result' from='capulet.it' to='juliet@capulet.it/balcony' id='3'>
|
|||
|
<x xmlns='https://xabber.com/protocol/auth-tokens#items'>
|
|||
|
<field var='1'>
|
|||
|
<client>xabber-web 2.3</client>
|
|||
|
<device>iMac Pro MacOS 10.14</device>
|
|||
|
<token-uid>
|
|||
|
024717297867c1d32714cadde305825a9909ef7c
|
|||
|
</token-uid>
|
|||
|
<expire>1536322632</expire>
|
|||
|
<ip>192.168.1.2</ip>
|
|||
|
<last-auth>1536322632</last-auth>
|
|||
|
</field>
|
|||
|
<field var='2'>
|
|||
|
<client>xabber-android 2.363</client>
|
|||
|
<device>Nokia Android 8.0</device>
|
|||
|
<token-uid>
|
|||
|
7dbf8541c4de1d24a0f748cc01f98a140100979a
|
|||
|
</token-uid>
|
|||
|
<expire>1536322632</expire>
|
|||
|
<ip>192.168.1.3</ip>
|
|||
|
<last-auth>1536322632</last-auth>
|
|||
|
</field>
|
|||
|
<field var='3'>
|
|||
|
<client>xabber-ios 1.8</client>
|
|||
|
<device>iphone 5s IOS 12.3.1</device>
|
|||
|
<token-uid>
|
|||
|
49975a48609793c5c93f5e9eab264f6706f04164
|
|||
|
</token-uid>
|
|||
|
<expire>1536322632</expire>
|
|||
|
<ip>192.168.1.3</ip>
|
|||
|
<last-auth>1536322632</last-auth>
|
|||
|
</field>
|
|||
|
<field var='4'>
|
|||
|
<client>xabber-desktop 1.2</client>
|
|||
|
<device>PC Arch Linux x86_64</device>
|
|||
|
<token-uid>
|
|||
|
86c763fcdca9b3372685ca4b258b1a207b9138f5
|
|||
|
</token-uid>
|
|||
|
<expire>1536322632</expire>
|
|||
|
<ip>192.168.1.5</ip>
|
|||
|
<last-auth>1536322632</last-auth>
|
|||
|
</field>
|
|||
|
</x>
|
|||
|
</iq>]]></example>
|
|||
|
</section2>
|
|||
|
<section2 topic='Token revocation' anchor='token_revocation'>
|
|||
|
<example caption='To revoke a token, the client sends to the server'><![CDATA[
|
|||
|
<iq type='set' from='juliet@capulet.it/balcony' to='capulet.it' id='4'>
|
|||
|
<revoke xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<token-uid>
|
|||
|
86c763fcdca9b3372685ca4b258b1a207b9138f5
|
|||
|
</token-uid>
|
|||
|
</revoke>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='If the client wants to delete several tokens, it needs to send them in a list'><![CDATA[
|
|||
|
<iq type='set' from='juliet@capulet.it/balcony' to='capulet.it' id='5'>
|
|||
|
<revoke xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<token-uid>
|
|||
|
49975a48609793c5c93f5e9eab264f6706f04164
|
|||
|
</token-uid>
|
|||
|
<token-uid>
|
|||
|
7dbf8541c4de1d24a0f748cc01f98a140100979a
|
|||
|
</token-uid>
|
|||
|
</revoke>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='On success, the server responds to the client'><![CDATA[
|
|||
|
<iq from='capulet.it' to='juliet@capulet.it/balcony' type='result' id='5'/>]]></example>
|
|||
|
<example caption='The server also sends notification'><![CDATA[
|
|||
|
<message type='headline' from='capulet.it' to='juliet@capulet.it/balcony' id='5'>
|
|||
|
<revoke xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<token-uid>
|
|||
|
49975a48609793c5c93f5e9eab264f6706f04164
|
|||
|
</token-uid>
|
|||
|
<token-uid>
|
|||
|
7dbf8541c4de1d24a0f748cc01f98a140100979a
|
|||
|
</token-uid>
|
|||
|
</revoke>
|
|||
|
</message>]]></example>
|
|||
|
<example caption='On failure, the server responds to the client'><![CDATA[
|
|||
|
<iq type='error' from='capulet.it' to='juliet@capulet.it/balcony' id='5'>
|
|||
|
<error code='400' type='modify'>
|
|||
|
<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
|||
|
</error>
|
|||
|
</iq>]]></example>
|
|||
|
</section2>
|
|||
|
<section2 topic='Notification about new token' anchor='notification_about_new_token'>
|
|||
|
<p>The server issues a new token and sends a message with a notification about the entrance from the new device.</p>
|
|||
|
<example><![CDATA[
|
|||
|
<message type='chat' from='capulet.it' to='juliet@capulet.it/balcony' id='msg1'>
|
|||
|
<x xmlns='https://xabber.com/protocol/auth-tokens'>
|
|||
|
<token-uid>dnbo3gasxia1agsj5nxzrlxr57ilibh9</token-uid>
|
|||
|
</x>
|
|||
|
<body>New login. Dear juliet@capulet.it, we detected a new login into your account from a new device on 01/09/2019 at 05:39:10 UTC
|
|||
|
Xabber Web 2.6.5.642
|
|||
|
PC Linux x86_64
|
|||
|
192.168.1.2
|
|||
|
If this wasn't you, go to Settings > XMPP Account > Active sessions and terminate suspicious sessions.
|
|||
|
</body>
|
|||
|
</message>]]></example>
|
|||
|
</section2>
|
|||
|
<section2 topic='Token revocation before bind' anchor='token_revocation_before_bind'>
|
|||
|
<p>During authentication process the client can revoke all tokens before bind. This might be necessary if someone has gained access to a client device and revokes all tokens immediately after connection, not giving a chance to account owner to revoke access.</p>
|
|||
|
<p>After passing all the authentication steps, the client makes a request to delete all tokens</p>
|
|||
|
<example><![CDATA[
|
|||
|
<iq type='set' to='capulet.it' id='6'>
|
|||
|
<revoke-all xmlns='https://xabber.com/protocol/auth-tokens'/>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='On success, the server responds to the client'><![CDATA[
|
|||
|
<iq from='capulet.it' type='result' id='6'/>]]></example>
|
|||
|
</section2>
|
|||
|
<section2 topic='Request token information' anchor='request_token_information'>
|
|||
|
<example caption='To view information about the token, the client sends a request to the server'><![CDATA[
|
|||
|
<iq type='get' from='juliet@capulet.it/balcony' to='capulet.it' id='7'>
|
|||
|
<query xmlns='https://xabber.com/protocol/auth-tokens#items'>
|
|||
|
<token>VkpTYqfpPcLpwciTRtgHaV7HLBC9O9kY</token>
|
|||
|
</query>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='The server responds with return full information about the token'><![CDATA[
|
|||
|
<iq type='result' from='capulet.it' to='juliet@capulet.it/balcony' id='7'>
|
|||
|
<x xmlns='https://xabber.com/protocol/auth-tokens#items'>
|
|||
|
<field var='1'>
|
|||
|
<client>xabber-web 2.3</client>
|
|||
|
<device>iMac Pro MacOS 10.14</device>
|
|||
|
<token-uid>
|
|||
|
024717297867c1d32714cadde305825a9909ef7c
|
|||
|
</token-uid>
|
|||
|
<expire>1536322632</expire>
|
|||
|
<ip>192.168.1.2</ip>
|
|||
|
<last-auth>1536322632</last-auth>
|
|||
|
</field>
|
|||
|
</x>
|
|||
|
</iq>]]></example>
|
|||
|
</section2>
|
|||
|
</section1>
|
|||
|
<section1 topic='Determining support' anchor='support'>
|
|||
|
<example caption='The client requests list of supported protocols from his server'><![CDATA[
|
|||
|
<iq type='get' from='juliet@capulet.it/balcony' to='capulet.it' id='1'>
|
|||
|
<query xmlns='http://jabber.org/protocol/disco#info'/>
|
|||
|
</iq>]]></example>
|
|||
|
<example caption='The server announces support for token protocol'><![CDATA[
|
|||
|
<iq type='result' from='capulet.it' to='juliet@capulet.it/balcony' id='1'>
|
|||
|
<query xmlns='http://jabber.org/protocol/disco#info'>
|
|||
|
...
|
|||
|
<feature var='https://xabber.com/protocol/auth-tokens'/>
|
|||
|
...
|
|||
|
</query>
|
|||
|
</iq>]]></example>
|
|||
|
</section1>
|
|||
|
<section1 topic='Security Considerations' anchor='security'>
|
|||
|
<p>The server is recording IP addresses used by user. While this enforces privacy during regular usage, making user aware of a malicious attempts to access his data, it also creates a privacy risk for a user if this data is leaked: it might help to determine user identity and location. Server operators should be warned about this risk and take measures against it.</p>
|
|||
|
</section1>
|
|||
|
<section1 topic='IANA Considerations' anchor='iana'>
|
|||
|
<p>This document requires no interaction with &IANA;.</p>
|
|||
|
</section1>
|
|||
|
<section1 topic='XMPP Registrar Considerations' anchor='registrar'>
|
|||
|
<section2 topic='Protocol Namespaces' anchor='ns'>
|
|||
|
<p>The ®ISTRAR; includes 'https://xabber.com/protocol/auth-tokens' and 'https://xabber.com/protocol/auth-tokens#items' in its registry of protocol namespaces (see &NAMESPACES;).</p>
|
|||
|
<ul>
|
|||
|
<li>https://xabber.com/protocol/auth-tokens</li>
|
|||
|
<li>https://xabber.com/protocol/auth-tokens#items</li>
|
|||
|
</ul>
|
|||
|
</section2>
|
|||
|
</section1>
|
|||
|
<section1 topic='XML Schemas' anchor='schemas'>
|
|||
|
<section2 topic='Namespace https://xabber.com/protocol/auth-tokens' anchor='schemas-x-tokens'>
|
|||
|
<code><![CDATA[
|
|||
|
<?xml version='1.0' encoding='UTF-8'?>
|
|||
|
|
|||
|
<xs:schema
|
|||
|
xmlns:xs='http://www.w3.org/2001/XMLSchema'
|
|||
|
targetNamespace='https://xabber.com/protocol/auth-tokens'
|
|||
|
xmlns='https://xabber.com/protocol/auth-tokens'
|
|||
|
elementFormDefault='qualified'>
|
|||
|
|
|||
|
<xs:annotation>
|
|||
|
<xs:documentation>
|
|||
|
The protocol documented by this schema is defined in
|
|||
|
XEP-xxxx: http://www.xmpp.org/extensions/xep-xxxx.html
|
|||
|
</xs:documentation>
|
|||
|
</xs:annotation>
|
|||
|
|
|||
|
<xs:element name='x'>
|
|||
|
<xs:complexType>
|
|||
|
<xs:sequence>
|
|||
|
<xs:element name='token' type='xs:string' minOccurs='0' maxOccurs='1'/>
|
|||
|
<xs:element name='token-uid' type='xs:string' minOccurs='0' maxOccurs='1'/>
|
|||
|
<xs:element name='expire' type='xs:unsigned' minOccurs='0' maxOccurs='1'/>
|
|||
|
</xs:sequence>
|
|||
|
</xs:complexType>
|
|||
|
</xs:element>
|
|||
|
|
|||
|
<xs:element name='issue'>
|
|||
|
<xs:complexType>
|
|||
|
<xs:sequence>
|
|||
|
<xs:element name='client' type='xs:string' minOccurs='1' maxOccurs='1'/>
|
|||
|
<xs:element name='device' type='xs:string' minOccurs='1' maxOccurs='1'/>
|
|||
|
<xs:element name='expire' type='xs:unsigned' minOccurs='0' maxOccurs='1'/>
|
|||
|
</xs:sequence>
|
|||
|
</xs:complexType>
|
|||
|
</xs:element>
|
|||
|
|
|||
|
<xs:element name='revoke'>
|
|||
|
<xs:complexType>
|
|||
|
<xs:sequence>
|
|||
|
<xs:element name='token-uid' type='xs:string' minOccurs='1' maxOccurs='unbounded'/>
|
|||
|
<xs:element ref='revoke-all' minOccurs='0' maxOccurs='unbounded'/>
|
|||
|
</xs:sequence>
|
|||
|
</xs:complexType>
|
|||
|
</xs:element>
|
|||
|
|
|||
|
<xs:element name='revoke-all'/>
|
|||
|
|
|||
|
</xs:schema>]]></code>
|
|||
|
</section2>
|
|||
|
<section2 topic='Namespace https://xabber.com/protocol/auth-tokens#items' anchor='schemas-auth-tokens-items'>
|
|||
|
<code><![CDATA[
|
|||
|
<?xml version='1.0' encoding='UTF-8'?>
|
|||
|
|
|||
|
<xs:schema
|
|||
|
xmlns:xs='http://www.w3.org/2001/XMLSchema'
|
|||
|
targetNamespace='https://xabber.com/protocol/auth-tokens#items'
|
|||
|
xmlns='https://xabber.com/protocol/auth-tokens#items'
|
|||
|
elementFormDefault='qualified'>
|
|||
|
|
|||
|
<xs:annotation>
|
|||
|
<xs:documentation>
|
|||
|
The protocol documented by this schema is defined in
|
|||
|
XEP-xxxx: http://www.xmpp.org/extensions/xep-xxxx.html
|
|||
|
</xs:documentation>
|
|||
|
</xs:annotation>
|
|||
|
|
|||
|
<xs:element name='query'>
|
|||
|
<xs:complexType>
|
|||
|
<xs:sequence>
|
|||
|
<xs:element name='token' type='xs:string' minOccurs='0' maxOccurs='1'/>
|
|||
|
</xs:sequence>
|
|||
|
</xs:complexType>
|
|||
|
</xs:element>
|
|||
|
|
|||
|
<xs:element name='x'>
|
|||
|
<xs:complexType>
|
|||
|
<xs:sequence>
|
|||
|
<xs:element ref='field' minOccurs='0' maxOccurs='unbounded'/>
|
|||
|
</xs:sequence>
|
|||
|
</xs:complexType>
|
|||
|
</xs:element>
|
|||
|
|
|||
|
<xs:element name='field'>
|
|||
|
<xs:complexType>
|
|||
|
<xs:sequence>
|
|||
|
<xs:attribute name='var' type='xs:string' use='required'/>
|
|||
|
<xs:element name='client' type='xs:string' minOccurs='1' maxOccurs='1'/>
|
|||
|
<xs:element name='device' type='xs:string' minOccurs='1' maxOccurs='1'/>
|
|||
|
<xs:element name='token-uid' type='xs:string' minOccurs='1' maxOccurs='1'/>
|
|||
|
<xs:element name='expire' type='xs:unsigned' minOccurs='1' maxOccurs='1'/>
|
|||
|
<xs:element name='ip' type='xs:string' minOccurs='1' maxOccurs='1'/>
|
|||
|
<xs:element name='last-auth' type='xs:unsigned' minOccurs='1' maxOccurs='1'/>
|
|||
|
</xs:sequence>
|
|||
|
</xs:complexType>
|
|||
|
</xs:element>
|
|||
|
|
|||
|
</xs:schema>]]></code>
|
|||
|
</section2>
|
|||
|
</section1>
|
|||
|
</xep>
|