Introduction
Sopher is a web development platform that allows you to develop end-to-end encrypted web applications.
At the same time the service Sopher is a communication platform for teams, client-side identity provider and authorization framework. This documentation explains how to use the Sopher Javascript API to work with a user’s identity, data, and services.
Sopher uses oAuth2 to implement authorization. The identity services are compatible with OpenID Connect to simplify user authentication.
Interactive documentation
This API documentation includes interactive elements to work with the API and real data.
For this documentation we use identities hosted at https://test.sopher.io. You can manage the documentation accounts here: Docs Account Management
Getting Started
Get the Sopher SDK via bower or npm
bower install sopherio-websdk --save
(not yet)
npm install @sopherio/websdk --save
(not yet)
or link or download from CDN
https://id.sopher.io/v1/sopher.js
If you want to make sure we do not change the SDK source code from under you feet you find corresponding subresource integrity hashes in this file:
https://id.sopher.io/v1/sri.json
Usage
include the Sopher SDK via script tag or via RequireJS
<script
src="lib/sopher.js"
integrity="sha384-<SRI-hash-sha384>"
></script>
// configure requirejs
require.config({
paths: {
sopher: '//id.sopher.io/v1/sopher.js'
}
});
// include the Sopher SDK as AMD module
require(['sopher'], (sopher) => {
sopher.init(<APP_ID_TOKEN>)
.then(() => {
// use the SDK
})
.catch(error => {
// perform error handling
})
})
You can include Sopher SDK via script tag or as UMD module.
Use of Promises
Using promises
sopher.init(<APP_ID_TOKEN>)
.then(() => sopher.getConnectedUser())
.then(userId => sopher.getUserDetails(userId))
.then(userInfo => {
// write user info to ui
})
.catch(error => {
// perform error handling
})
The Sopher SDK uses native promises to respond asynchronously. Every method returns a native Promise
even if the call returns synchronously. We have decided to do so to not mix the style of javascript between the calls.
In addition to this you can chain all Sopher SDK calls using promise chaining.
Initialization
Register an app to obtain an app id token
{
"name": "Sopher API Documentation",
"id": "07c6dda9-bf56-4a1f-b199-5d2c63e36b74",
"origin": "https://doc.sopher.io",
"email": "contact@sopher.io",
"scopes": ["https://sopher.io/social", "https://sopher.io/userdata"],
"logo": "https://doc.sopher.io/images/volume_256.png"
}
Sopher uses JWT app tokens to authenticate your client and allow access to the account API. You can register a new Sopher app at our developer portal.
Your id token is a JWT signed by sopher.io. It is bound to the registered origin. It contains information about the scopes of your application, your contact email, and application icon.
We recommend to register test client ids for, e.g., localhost
, 127.0.0.1:8080
, or *.dev
origins. We sign ids on these origins without any restrictions on scopes or services.
sopher.init
// init sopher with your app id token
sopher.init('<APP_ID_TOKEN>')
.then(() => {
// do stuff with sopher accounts
})
.catch(error => {
// error handling
});
The Sopher SDK has to be initialized with the app id token before any function can be used:
sopher.init(<APP_ID_TOKEN>): Promise
returns a Promise
that resolves when the SDK has been successfully initialized. The Sopher SDK injects an iFrame into the page that handles the communication with the user’s account.
There are three optional arguments to sopher.init()
sopher.init(<APP_ID_TOKEN>, {coreHost, accountHost, namespace} = {}): Promise
coreHost
defines the host where the Sopher core application is hosted. At the same time it defines the origin for user account data within the storage of the browser. Defaults to https://id.sopher.io/v1
.
accountHost
hosts the account management and authorization application, defaults to https://account.sopher.io
.
namespace
is a string that is prefixed to the database names of user data. Defaults to the empty string.
For the interactive examples of this documentation we use https://test.sopher.io/v1
as core host and APIDOC
as namespace. You can check the contents of the user databases in the developer tools of your browser or access the manage the test accounts by appending these options to the accountHost
: Docs Account Management. These options are for testing purposes only, in a production environment you must leave them out.
sopher.dispose
// dispose the Sopher SDK
sopher.dispose()
.then(() => {
// shutdown complete
})
.catch(error => {
// an error occurred
});
sopher.dispose(): Promise
Removes the SDK from the page. In particular the core iframe is removed and any background activity is stopped.
This method should be used in a unit-testing environment as a part of the tear-down phase of a test.
sopher.reset
sopher.reset(): Promise
Resets the SDK. Any user information is deleted, the connected user is disconnected.
sopher.reload
sopher.reload(): Promise
Loads the internal SDK state from local storage and session storage.
sopher.getVersion
// obtain the Sopher core version
sopher.getVersion()
.then(version => {
// do something with version
});
sopher.getVersion(): Promise
Returns a Promise with the version number of the Sopher core that is connected to the page.
sopher.setInitTimeout
sopher.setInitTimeout(timeout)
Sets the timeout
in milliseconds to be used in sopher.init()
calls. It is the time that we wait for the sopher core to get loaded and respond.
sopher.setApiTimeout
sopher.setApiTimeout(timeout)
Sets the timeout for API calls, e.g., sopher.social.getContacts()
.
Authorization
All methods in this section are contained in the namespace sopher.auth
. It allows an app to connect to its users and manage authorization and session information.
sopher.auth.connect
sopher.auth.connect()
.then(userId => {
// the user with the id userId is connected
})
.catch(error => {
// perform error handling
});
Returns the base64 encoded user id of the connected user, e.g.:
'Vfh1bEUSl6pkfarfPu9pFMoiGzlUV8LnbKatm6zTp_U'
sopher.auth.connect(): Promise
The function sopher.auth.connect()
connects a user to the app. It returns a Promise
that resolves with the userid
of the connected user or rejects with an error if the user cancels the authorization process.
On connection two tokens are issued and stored by the SDK: An authorization token authToken
and a sessionToken
. The authToken
is stored in localStorage, the sessionToken
is stored in sessionStorage. Both tokens are managed my the SDK. It uses the authToken
to refresh the sessionToken
. The sessionToken
is used to issue authorized API calls.
sopher.auth.connect(userId): Promise
connects the user with id userid
and obtains a session. The previous connected user is disconnected.
result
sopher.auth.disconnect
sopher.auth.disconnect(): Promise
Disconnects the connected user from the app. Subsequent calls to the user related API will fail as there is no active session.
sopher.auth.getConnectedUser
sopher.auth.getConnectedUser(): Promise
Returns a Promise
that resolves with the connected user’s id. If no user is connected null
is returned.
result
sopher.auth.getUserIds
sopher.auth.getUserIds()
.then(userIds => {
// obtain known user ids
})
.catch(error => {
// perform error handling
});
Returns an array of app user ids
[
"BrcvcBoj3gfQAp8Sk8TlopJrYHXLjzXgV7_h-L4NqUo",
"YyVp4w3nvLagc4RUXTrgJ7ocqkeJUXi7i5023ENJYYo",
"nGUdp3T3IafOLy0Z6-GCn2XSiTAE479Mgp-JMkTQqlM",
"wtpej7VdHVMfmEnXY34xTyOEO9OnVtxNPGZlx_Rt9RM"
]
sopher.auth.getUserIds(): Promise
Returns an array with the ids of the users of this app.
result
sopher.auth.removeUser
sopher.auth.removeUser(userId): Promise
removes a user from the app. Discards previously obtained authorization of the user with is userId
. A user can reauthorize the application to connect again.
sopher.auth.isAuthorizationValid
sopher.auth.isAuthorizationValid(userId): Promise
returns a native Promise
that resolves with true
or false
if there is a valid authorization for the given userId
. A user can revoke an authorization, use this method to check if a previously granted authorization is still valid.
result
sopher.auth.validateUsers
sopher.auth.validateUsers(): Promise
validates the authorization of all users and removes the users from the application that revoked the authorization.
result
sopher.auth.getUser
sopher.auth.getUser(userId)
.then(user => {
// render user details
})
.catch(error => {
// perform error handing
});
The user details contain the display name, username, and avatar image of the user
{
"id": "wtpej7VdHVMfmEnXY34xTyOEO9OnVtxNPGZlx_Rt9RM",
"name": "Docu Test User",
"username": "docu1",
"avatar": "data:image/png;base64,iVBORw0..."
}
sopher.auth.getUser(userId): Promise
Obtains information about a connected user with user id userId
. This information is not protected by a scope
or authorized session. This is intended to be used to render a list of all application users independent of active sessions.
result
sopher.auth.getAuthorization
sopher.auth.getAuthorization(userId)
.then(auth =>) {
// show authorization
})
.catch(error => {
// perform error handling
});
Returns the the user’s authorization information
{
"scopes_granted": [
"https://sopher.io/social",
"https://sopher.io/userdata"
],
"origin": "https://doc.sopher.io",
"publicKey": {
"alg": "RS256",
"e": "AQAB",
"ext": true,
"key_ops": [
"verify"
],
"kty": "RSA",
"n": "1EOKeHMasJBx2iY3yfwW3NkWmJeEf9u1XIBr2-7QrlzXtNJiEtfbb3a3CKtFGAD0cfSqcR_j3f-Cc0oAgo02rnkXhu_GbLgVdzA5NeWWex9b4yFODFDlTaBagkY1MsU3Nr7ITGqOaHTd7WswE8R4hcl5xO9HyrPXdPtap4L1MPzMUgyVsOjo0DnlgRRl7C3PtPKCn4FH5Sy8v_e83cM7I4ZR6zTN_XxTWMGXdR5v69b54TFYCktuT99y0Xqcb07M51AEHiDCiCDmvrCPqWaqC__wZO5Az0yMwOH5RbvznAQMeJP28inOIdKpEqNUO1ylpt9imd46hdx5lOk9S4smXw"
},
"appuser": "Dg3DqSw9Mf9UF35bFTdG-gdHQNlMTgW-kyn0cXeVdBY"
}
sopher.auth.getAuthorization(userId): Promise
returns the decoded information of the user’s authorization token. Authorization tokens are JWT style tokens that contain information about the user’s relation to the app.
result
sopher.auth.addAuthorizationToken
sopher.auth.addAuthorizationToken(token): Promise
adds an authorization token to the app. This corresponds to adding a new user to the app.
sopher.auth.getAuthorizationToken
sopher.auth.getAuthorizationToken(userId): Promise
returns a Promise
that resolves with the user’s authorization token.
Use sopher.auth.getAuthorization
to get a decoded version of the token.
result
You might want to work with JWTs in more detail. Check out the JWT debugger at jwt.io.
User
Functions in this section are contained in the namespace sopher.user
. To access this part of the API the client needs to be registered and authorized for the
https://sopher.io/userdata
scope.
sopher.user.getUser
sopher.user.getUser()
.then(user => {
// use user data
})
.catch (error => {
// error handling
});
Returns details for the connected user:
{
"SID": "a35a5766737d93688bce6c0149add079e9a348b5aca9eee6ff79af0bb2548972",
"name": "Frank Sinatra",
"username": "frank",
"email": "frank.sinatra@gmail.com",
"avatar": "data:image/jpeg,base64,<image data>"
}
sopher.user.getUser() : Promise
This function returns user details for the connected user.
result
Social
The social API is contained in the namespace sopher.social
. An application
that wants to use this API needs to be registered and authorized for the
https://sopher.io/social
scope.
sopher.social.getIdentityToken
sopher.social.getIdentityToken()
.then(token => {
// assemble a public link
})
.catch(error => {
// error handling
});
Returns the identity token of the connected user. The identity token is used to identify a user and send invitations.
An application that uses social interactions should implement a user handshake via identity tokens.
result
sopher.social.loadIdentityProfile
sopher.social.loadIdentityProfile(identityToken)
Loads the identity profile for an identity given by an identity token.
result
sopher.social.inviteContact
sopher.social.inviteContact(identityToken)
Invite a contact with its identity token.
result
sopher.social.getContact
sopher.social.getContact(SID)
Get the contact with SID
{
"SID": "7c7e93b63243187378f44b6dc341897738ead2a8b681e7a85d87398c9927215a",
"name": "Test Contact",
"username": "test42@test.com",
"email": "test42@test.com",
"avatar": "data:image/png;base64,..."
}
result
sopher.social.getContacts
sopher.social.getContacts()
Get all contacts of the connected user
result
sopher.social.deleteContact
sopher.social.deleteContact(SID)
Removes a contact with id SID
from the user’s account.
Revoke all contact permissions.
sopher.social.getMessages
[
{
"senderSID": "7c7e93b63243187378f44b6dc341897738ead2a8b681e7a85d87398c9927215a",
"receiverSID": "a35a5766737d93688bce6c0149add079e9a348b5aca9eee6ff79af0bb2548972",
"subject": "CHAT",
"status": "PROCESSED",
"timestamp": 1462485019169,
"body": "Hi, how are you?"
},
{
"senderSID": "a35a5766737d93688bce6c0149add079e9a348b5aca9eee6ff79af0bb2548972",
"receiverSID": "7c7e93b63243187378f44b6dc341897738ead2a8b681e7a85d87398c9927215a",
"subject": "CHAT",
"status": "PROCESSED",
"timestamp": 1462485036344,
"body": "I'm fine, thanks!"
}
]
sopher.social.getMessages(SID)
Get all messages related to the contact with id SID
.
result
sopher.social.sendTextMessage
sopher.social.sendTextMessage(
'978b161bc59a7a8100831a69baaef150fe63a35b6e198b35d102dfacb09e83a0',
'Hallo 😀'
)
.error(error => {
// error handling
})
sopher.social.sendTextMessage(receiverSID, text)
{
"id": "118aa93b-7e5a-4a14-a0db-6112ccdced0a",
"senderSID": "61f9b85e75add93a33c9beecde398b7ce5f2b52647b1877eb930375b7182c269",
"receiverSID": "978b161bc59a7a8100831a69baaef150fe63a35b6e198b35d102dfacb09e83a0",
"status": "NEW",
"timestamp": 1576742841335,
"subject": "CHAT",
"body": "Hallo 😀"
}
Send a message to a contact.
Parameters
message
a message object:
result
Persistence
User data persistence, coming soon…
APIs
3rd-party APIs via sopher SDK, coming soon…
OAuth Authorization
Sopher accounts can act as Self-Issued OpenID Connect provider: “OpenID Connect is a simple identity layer on top of the OAuth 2.0 protocol. It enables Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.”
Sopher implements the OpenID Connect Protocol on top of the Implicit Flow of the OAuth 2.0 specification [1, 2]. A sopher account acts as a Self-Issued Identity Provider [3].
Authorization Request
The sopher OAuth 2.0 authorization end point can be accessed at
https://account.sopher.io#auth
Parameters
https://account.sopher.io#auth?
client_id=http://clientorigin.com/auth&
registration=<JSON with client_id_token>&
response_type=id_token&
scope=openid&
...
Parameters are appended to this end point URI as part of the fragment of the URL, e.g.,
the registration parameter JSON object
{
"client_id_token": "eyJhbGciOiJSUzI1NiI..."
}
parameter | value |
---|---|
response_type , |
OAuth 2.0 Response Type value that determines the authorization processing flow to be used. Sopher only accepts the value id_token . |
client_id |
Sopher uses the Sefl-Issued Implicit OpenID Connect flow. Following the specs the client_id contains the redirect URI of the client [3]. |
registration |
An url encoded JSON object with the attribute client_id_token containing the client ID obtained via registration at http://dev.sopher.io. This parameter must be present in order to authenticate your client and pass information such a the name and client logo of your application. |
scope |
A list of space-delimited, case-sensitive strings of scope Parameters [4]. Currently only the openid scope is supported. |
nonce |
String value used to associate a Client session with an id_token , and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the id_token . Sufficient entropy must be present in the nonce values used to prevent attackers from guessing values. |
state |
Opaque value used to maintain state between the request and the callback. Typically, Cross-Site Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this parameter with a browser cookie. This value is round tripped to the Authorization Response as an url parameter. |
prompt |
Optional, currently only select_account is supported which is the default. |
[1] OpenID Connect Core 1.0 - Authentication using the Implicit Flow
[2] The OAuth 2.0 Authorization Framework - Implicit Grant
[3] OpenID Connect Core 1.0 - Self-Issued OpenID Provider
[4] OpenID Connect Core 1.0 - Requesting Claims using Scope Values
Authorization Response
On successful authentication and consent by the user sopher redirects the browser to the URI contained in the client_id
parameter. The id_token
and further parameters are contained in the fragment component of the URI.
Parameters
parameter | value |
---|---|
id_token |
The ID Token is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when using a Client, and potentially other requested Claims. The ID Token is represented as a JSON Web Token (JWT) |
state |
OAuth 2.0 state value. Must be verified. |
The id_token
is a JSON Web Token (JWT) containing user information. In particular the attributes used by sopher are
{
"iss": "https://self-issued.me",
"sub": "lmGlAlIG8v-ip47K5sfBrFAXlKdOEBFVQs-VjEEJtkg",
"sub_jwk": {
"alg": "RS256",
"e": "AQAB",
"ext": true,
"key_ops": [
"verify"
],
"kty": "RSA",
"n": "yv1ikO-rbN5Vsb-AFdaDvs0IXyuYIiN4HE4N14iXjpY..."
},
"aud": "https://test.sopher.dev/oauth",
"exp": 1468432119,
"iat": 1468431819,
"auth_time": 1468431819,
"nonce": "my nonce"
}
attribute | value |
---|---|
iss |
Is equal to https://self-issued.me |
sub |
The appuser id. This value is the thumbprint of the public key sub_jwk . |
sub_jwk |
Public key used to sign this id_token . |
aud |
Is equal to the redirect_uri of the request (The value passed via the client_id parameter). |
exp |
Expiration date in seconds from 1970 |
iat |
“Issued at” timestamp. |
auth_time |
The timestamp of the first authorization for this audience. |
nonce |
The nonce passed in the request |
Validation of the response
The validation of the id_token
obtained in the response is performed along the lines described in the OpenID Connect specification for the Self-Issued Identity Provider case.
It basically amounts to verifying the id_token
signed JWT and checking the thumbprint of the public key against the sub
claim of the response.