Skip to content

Commit

Permalink
more work on changing email and phone #
Browse files Browse the repository at this point in the history
  • Loading branch information
or-else committed Jun 6, 2019
1 parent c5c61bb commit 6ae4e70
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 56 deletions.
74 changes: 38 additions & 36 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
- [Logging in](#logging-in)
- [Changing Authentication Parameters](#changing-authentication-parameters)
- [Resetting a Password, i.e. "Forgot Password"](#resetting-a-password-ie-forgot-password)
- [Credentials](#credentials)
- [Changing Credentials](#changing-credentials)
- [Credential Validation](#credential-validation)
- [Access control](#access-control)
- [Topics](#topics)
- [`me` topic](#me-topic)
Expand Down Expand Up @@ -223,38 +222,16 @@ where `jdoe@example.com` is an earlier validated user's email.

If the email matches the registration, the server will send a message using specified method and address with instructions for resetting the secret. The email contains a restricted security token which the user can include into an `{acc}` request with the new secret as described in [Changing Authentication Parameters](#changing-authentication-parameters).

### Credentials
### Credential Validation

Server may be optionally configured to require certain credentials associated with the user accounts and authentication scheme. For instance, it's possible to require user to provide a unique email or a phone number, or to solve a captcha as a condition of account registration.
Server may be optionally configured to require validation of certain credentials associated with the user accounts and authentication scheme. For instance, it's possible to require user to provide a unique email or a phone number, or to solve a captcha as a condition of account registration.

The server supports verification of email and phone numbers out of the box. Verification of emails is mostly functional, verification of phone numbers is not functional because a commercial subscription is needed in order to be able to send text messages (SMS).

Credentials are added or removed by sending an `{acc}` message, verified by sending either a `{login}` or an `{acc}` message.

#### Changing Credentials

Credentials can be added or deleted by sending an `{acc}` message:
```js
acc: {
id: "1a2b3", // string, client-provided message id, optional
user: "usr2il9suCbuko", // user being affected by the change, optional
token: "XMg...g1Gp8+BO0=", // authentication token if the session is
// not yet authenticated, optional.
cred: [
{
meth: "email", // string, verification method, e.g. "email", "tel", "recaptcha", etc.
val: "alice@example.com", // string, credential to update such as email or phone
resp: "178307", // string, verification response, optional
params: { ... } // parameters, specific to the verification method, optional
},
...
]
}
```
To remove an existing credential set `resp` to a single Unicode DEL character "␡" (`\u2421`): `resp: "␡"`.
The server supports verification of email out of the box with just a configuration change. is mostly functional, verification of phone numbers is not functional because a commercial subscription is needed in order to be able to send text messages (SMS).

If certain credentials are required, then user must maintain them in validated state at all times. It means if a required credential has to be changed, the user must first add and validate the new credential and only then remove the old one.

Credentials are initially assigned at registration time by sending an `{acc}` message, added using `{set topic="me"}`, deleted using `{del topic="me"}`, and queries by `{get topic="me"}` messages. Credentials are verified by the client by sending either a `{login}` or an `{acc}` message.


### Access control

Expand Down Expand Up @@ -625,15 +602,15 @@ acc: {
tags: ["alice johnson",... ], // array of tags for user discovery; see 'fnd' topic for
// details, optional (if missing, user will not be discoverable other than
// by login)
cred: [
cred: [ // account credentials which require verification, such as email or phone number.
{
meth: "email", // string, verification method, e.g. "email", "tel", "recaptcha", etc.
val: "alice@example.com", // string, credential to verify such as email or phone
resp: "178307", // string, verification response, optional
params: { ... } // parameters, specific to the verification method, optional
},
...
], // account credentials which require verification, such as email or phone number.
],

desc: { // object, user initialisation data closely matching that of table
// initialisation; optional
Expand Down Expand Up @@ -730,6 +707,16 @@ sub: {
// Optional update to tags (see fnd topic description)
tags: [ // array of strings
"email:alice@example.com", "tel:1234567890"
],

// Optional update to validated credentials.
cred: [
{
meth: "email", // string, verification method, e.g. "email", "tel", "recaptcha", etc.
val: "alice@example.com", // string, credential to verify such as email or phone
resp: "178307", // string, verification response, optional
params: { ... } // parameters, specific to the verification method, optional
},
]
},

Expand Down Expand Up @@ -836,8 +823,8 @@ Query topic for metadata, such as description or a list of subscribers, or query
get: {
id: "1a2b3", // string, client-provided message id, optional
topic: "grp1XUtEhjv6HND", // string, name of topic to request data from
what: "sub desc data del", // string, space-separated list of parameters to query;
// unknown strings are ignored; required
what: "sub desc data del cred", // string, space-separated list of parameters to query;
// unknown values are ignored; required

// Optional parameters for {get what="desc"}
desc: {
Expand Down Expand Up @@ -907,6 +894,9 @@ Query message deletion history. Server responds with a `{meta}` message containi

See [Public and Private Fields](#public-and-private-fields) for `private` and `public` format considerations.

* `{get what="cred"}`

Query [credentials](#credentail-validation). Server responds with a `{meta}` message containing an array of credentials. Supported only for `me` topic only.

#### `{set}`

Expand Down Expand Up @@ -951,9 +941,9 @@ del: {
id: "1a2b3", // string, client-provided message id, optional
topic: "grp1XUtEhjv6HND", // string, topic affected, required for "topic", "sub",
// "msg"
what: "msg", // string, one of "topic", "sub", "msg", "user"; what to delete - the
// entire topic, a subscription, some or all messages, a user;
// optional, default: "msg"
what: "msg", // string, one of "topic", "sub", "msg", "user", "cred"; what
// to delete - the entire topic, a subscription, some or all messages,
// a user, a credential; optional, default: "msg"
hard: false, // boolean, request to hard-delete vs mark as deleted; in case of
// what="msg" delete for all users vs current user only;
// optional, default: false
Expand All @@ -980,6 +970,10 @@ Deleting a topic deletes the topic including all subscriptions, and all messages

Deleting a user is a very heavy operation. Use caution.

`what="cred"`

Delete credential. Only validated credentials and those with no attempts at validation are deleted. Credentials with failed attempts at validation are hidden.


#### `{note}`

Expand Down Expand Up @@ -1149,6 +1143,14 @@ meta: {
tags: [ // array of tags that the topic or user (in case of "me" topic) is indexed by
"email:alice@example.com", "tel:+1234567890"
],
cred: [ // array of user's credentials
{
meth: "email", // string, validation method
val: "alice@example.com", // string, credential value
done: true // validation status
},
...
],
del: {
clear: 3, // ID of the latest applicable 'delete' transaction
delseq: [{low: 15}, {low: 22, hi: 28}, ...], // ranges of IDs of deleted messages
Expand Down
5 changes: 5 additions & 0 deletions rest-auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ def link():
def upd():
return jsonify({'err': 'unsupported'})

@app.route('/rtagns', methods=['POST'])
def rtags():
# Return dummy namespace "rest" and "email"
return jsonify({'strarr': ['rest', 'email']})

@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'err': 'not found'}), 404)
Expand Down
5 changes: 5 additions & 0 deletions server/auth/anon/auth_anon.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func (authenticator) DelRecords(uid types.Uid) error {
return nil
}

// RestrictedTags returns tag namespaces restricted by this authenticator (none for anonymous).
func (authenticator) RestrictedTags() ([]string, error) {
return nil, nil
}

func init() {
store.RegisterAuthScheme("anonymous", &authenticator{})
}
3 changes: 3 additions & 0 deletions server/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,7 @@ type AuthHandler interface {

// DelRecords deletes (or disables) all authentication records for the given user.
DelRecords(uid types.Uid) error

// RestrictedTags returns the tag namespaces which are restricted by this authenticator.
RestrictedTags() ([]string, error)
}
14 changes: 14 additions & 0 deletions server/auth/basic/auth_basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func parseSecret(bsecret []byte) (uname, password string, err error) {

// Init initializes the basic authenticator.
func (a *authenticator) Init(jsonconf, name string) error {
if name == "" {
return errors.New("auth_basic: authenticator name cannot be blank")
}

if a.name != "" {
return errors.New("auth_basic: already initialized as " + a.name + "; " + name)
}
Expand Down Expand Up @@ -258,10 +262,20 @@ func (authenticator) GenSecret(rec *auth.Rec) ([]byte, time.Time, error) {
return nil, time.Time{}, types.ErrUnsupported
}

// DelRecords deletes saved authentication records of the given user.
func (a *authenticator) DelRecords(uid types.Uid) error {
return store.Users.DelAuthRecords(uid, a.name)
}

// RestrictedTags returns tag namespaces restricted in this config.
func (a *authenticator) RestrictedTags() ([]string, error) {
var tags []string
if a.addToTags {
tags = []string{a.name}
}
return tags, nil
}

func init() {
store.RegisterAuthScheme("basic", &authenticator{})
}
22 changes: 22 additions & 0 deletions server/auth/rest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Request and response payloads are formatted as JSON. Some of the request or resp
- [`upd` Update authentication record.](#upd-update-authentication-record)
- [Sample request](#sample-request)
- [Sample response](#sample-response)
- [`rtagns` Get a list of restricted tag namespaces.](#get-a-list-of-restricted-tag-namespaces)
- [Sample request](#sample-request)
- [Sample response](#sample-response)

<!-- /TOC -->

Expand Down Expand Up @@ -83,6 +86,7 @@ Request and response payloads are formatted as JSON. Some of the request or resp
"byteval": "Ym9iOmJvYjEyMw==", // array of bytes, optional
"ts": "2018-12-04T15:17:02.627Z", // time stamp, optional
"boolval": true, // boolean value, optional
"strarr": ["abc", "def"], // array of strings, optoional
"newacc": { // data to use for creating a new account.
// Default access mode
"auth": "JRWPS",
Expand Down Expand Up @@ -294,3 +298,21 @@ If accounts are managed by the server, the server should respond with an error `
```json
{}
```

### `rtagns` Get a list of restricted tag namespaces.

Server may enforce certain tag namespaces to be restricted, i.e. not editable by the user.

#### Sample request
```json
{
"endpoint": "rtagns",
}
```

#### Sample response
```json
{
"strarr": ["basic", "email", "tel"]
}
```
16 changes: 15 additions & 1 deletion server/auth/rest/auth_rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ type authenticator struct {
useSeparateEndpoints bool
}

// Request to the server.
type request struct {
Endpoint string `json:"endpoint"`
Name string `json:"name"`
Record *auth.Rec `json:"rec,omitempty"`
Secret []byte `json:"secret,omitempty"`
}

// User initialization data when creating a new user
// User initialization data when creating a new user.
type newAccount struct {
// Default access mode
Auth string `json:"auth,omitempty"`
Expand All @@ -47,6 +48,7 @@ type newAccount struct {
Private interface{} `json:"private,omitempty"`
}

// Response from the server.
type response struct {
// Error message in case of an error.
Err string `json:"err,omitempty"`
Expand All @@ -58,6 +60,8 @@ type response struct {
TimeVal time.Time `json:"ts,omitempty"`
// Boolean value
BoolVal bool `json:"boolval,omitempty"`
// String slice value
StrSliceVal []string `json:"strarr,omitempty"`
// Account creation data
NewAcc *newAccount `json:"newacc,omitempty"`
}
Expand Down Expand Up @@ -225,6 +229,16 @@ func (a *authenticator) DelRecords(uid types.Uid) error {
return err
}

// RestrictedTags returns tag namespaces restricted by the server.
func (a *authenticator) RestrictedTags() ([]string, error) {
resp, err := a.callEndpoint("rtagns", nil, nil)
if err != nil {
return nil, err
}

return resp.StrSliceVal, nil
}

func init() {
store.RegisterAuthScheme("rest", &authenticator{})
}
5 changes: 5 additions & 0 deletions server/auth/token/auth_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ func (authenticator) DelRecords(uid types.Uid) error {
return nil
}

// RestrictedTags returns tag namespaces restricted by this authenticator (none for token).
func (authenticator) RestrictedTags() ([]string, error) {
return nil, nil
}

func init() {
store.RegisterAuthScheme("token", &authenticator{})
}
28 changes: 17 additions & 11 deletions server/datamodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,6 @@ type MsgCredClient struct {
Params interface{} `json:"params,omitempty"`
}

// MsgCredServer is an account credential such as email or phone number.
type MsgCredServer struct {
// Credential type, i.e. `email` or `tel`.
Method string `json:"meth,omitempty"`
// Credential value, i.e. `user@example.com` or `+18003287448`
Value string `json:"val,omitempty"`
// Indicates that the credential is validated.
Done bool `json:"done,omitempty"`
}

// MsgSetQuery is an update to topic metadata: Desc, subscriptions, or tags.
type MsgSetQuery struct {
// Topic metadata, new topic & new subscriptions only
Expand Down Expand Up @@ -152,7 +142,7 @@ type MsgClientLogin struct {
Scheme string `json:"scheme,omitempty"`
// Shared secret
Secret []byte `json:"secret"`
// Credntials to verify (email or phone or captcha etc.)
// Credntials being verified (email or phone or captcha etc.)
Cred []MsgCredClient `json:"cred,omitempty"`
}

Expand Down Expand Up @@ -182,6 +172,7 @@ const (
constMsgDelMsg
constMsgDelSub
constMsgDelUser
constMsgDelCred
)

func parseMsgClientMeta(params string) int {
Expand Down Expand Up @@ -218,6 +209,8 @@ func parseMsgClientDel(params string) int {
return constMsgDelSub
case "user":
return constMsgDelUser
case "cred":
return constMsgDelCred
default:
// ignore
}
Expand Down Expand Up @@ -269,11 +262,14 @@ type MsgClientDel struct {
// * "topic" to delete the topic
// * "sub" to delete a subscription to topic.
// * "user" to delete or disable user.
// * "cred" to delete credential (email or phone)
What string `json:"what"`
// Delete messages with these IDs (either one by one or a set of ranges)
DelSeq []MsgDelRange `json:"delseq,omitempty"`
// User ID of the user or subscription to delete
User string `json:"user,omitempty"`
// Credential to delete
Cred *MsgCredClient `json:"cred,omitempty"`
// Request to hard-delete objects (i.e. delete messages for all users), if such option is available.
Hard bool `json:"hard,omitempty"`
}
Expand Down Expand Up @@ -326,6 +322,16 @@ type MsgLastSeenInfo struct {
UserAgent string `json:"ua,omitempty"`
}

// MsgCredServer is an account credential such as email or phone number.
type MsgCredServer struct {
// Credential type, i.e. `email` or `tel`.
Method string `json:"meth,omitempty"`
// Credential value, i.e. `user@example.com` or `+18003287448`
Value string `json:"val,omitempty"`
// Indicates that the credential is validated.
Done bool `json:"done,omitempty"`
}

// MsgAccessMode is a definition of access mode.
type MsgAccessMode struct {
// Access mode requested by the user
Expand Down
Loading

0 comments on commit 6ae4e70

Please sign in to comment.