saaslify v0.0.9
Saaslify
- Saaslify
How to use the SDK?
We will just touch JavaScript Client SDK and NodeJS
import { Saaslify } from 'saaslify'
const saaslify = Saaslify({ saasId: 'your-awesome-app' })
...
or use our CDN
const script = document.createElement('script')
script.src = "https://unpkg.com/saaslify"
script.async = true
script.onload = () => {
const saaslify = window.Saaslify({ saasId: 'your-awesome-app' })
...
}
document.head.appendChild(script)
More efficient use of CDN
Authentication - Who am I?
Recipe 1 - Login Button
We assume you have a button some where
<span id="login" data-provider="" data-scopes="">login with github</span>
document.querySelector(`#login`).addEventListener('click', async (e) => {
const loginURL = await saalify.user.getLoginUrl({
provider: e.target.dataset['provider'],
scopes: e.target.dataset['scopes'].split(';'),
callbackURL: document.location.href // redirect to this page again
})
window.location.href === loginURL
})
Recipe 2 - Check whether is loggedin
const user = await saalify.user.getUser({
provider: e.target.dataset['provider'],
scopes: e.target.dataset['scopes'].split(';')
})
const isLoggedIn = Boolean(user)
Recipe 3 - Automatic redirect if not loggedin
const user = await Saalify.user.getUser()
const isLoggedIn = Boolean(user)
if (!isLoggedIn) {
const loginURL = await Saalify.user.getLoginUrl({
provider: e.target.dataset['provider'],
scopes: e.target.dataset['scopes'].split(';')
})
window.location.replace(loginURL) // `replace` overwrites the browser history
}
Authorization - What can I access?
Pure frontend applications
const user = await saalify.user.getUser()
Returns
Recipe for mixed frontend and backend authorization
In your browser
const body = {
bananas: 5
}
Saalify.user.fetch(`https://your.api-endpoint.com/order`, {
method: 'POST',
body: body, // remark *
headers: {
'content-type': 'application/json',
'accept': 'application/json'
}
})
Our fetch method differs from the normal in that we transform the body and add an additional field.
{
"jwt": "...",
"bananas": 5
}
Here is an example of a receiving node js server:
import * as express from 'express'
const app = express();
app.use(express.json());
app.post('/order', async (request, response) => {
const { jwt, bananas } = request
try {
const userData = await saaslify.user.jwtVerify(jwt)
const bananaPlan = userData.products.find(x => x.plan.name === 'banana')
if (bananaPlan) {
// ... order 5 bananas
response.status(201).json({ data })
return
} else {
response.status(201).json({ error: `User ${jwt.userId} has no plan "banana".` })
return
}
} catch (e) {
response.status(401).json({ error: e.message })
return
}
})
app.listen(3000, () => {
console.log(`Server is listening at port 3000`)
})
Other server side programming language
The only part which we have to use Saaslify library is:
await Saaslify.jwtVerify(jwt)
Recipe for machine to machine authorization
For cli or server to server applications, you will need some sort of api keys.
Either use our custom user management to allow users to generate machine tokens or build your own frontend with our Client SDK. User need to be logged in for this.
const token = await Saaslify.user.generateMachineToken({ name: 'terminator' })
Machines fetch just like browsers:
const machine = Saaslify.auth({ token, name, userId })
machine.fetch(`https://your.api-endpoint.com/order`, {
method: 'POST',
body: body, // remark *
headers: {
'content-type': 'application/json',
'accept': 'application/json'
}
})
You can double check on the server whether it is a machine jwt you are communicating with
const machineName = await Saaslify.user.jwtVerify(jwt).then(x => x.machine?.name)
const isMachine = Boolean(machineName)
Remarks: Security & limitations
Other Backend langueges
For other programming language, we you can simply request a JWT
curl https://auth-(dev|prod).saaslify.io/${saas_provider}/user/${user_id}/machine/${name}
-X GET
-H "X-SAASLIFY-MACHINE_AUTH: ${token}"
{
"jwt": "..."
}
Send your requests against the real server and verify them against the following public keys
curl https://auth-(dev|prod).saaslify.io/${saas_provider}/.wellknown
-X GET
-H "X-SAASLIFY-MACHINE_AUTH: ${token}"
[{
"privateKeyId": "...",
"timestamp": 0,
"publicKey": ""
}]
Please check the library section of jwt.io.
If you have a working solution in another language, please make a PR.
Key rotations
We rotate keys on a regular bases. Keys which should be active can be found here
Never persist the JWT
The client SDK hides away the jwt away. It is only safe to keep them in memory.
Custom fetch
The body in the fetch method is enriched with jwt
, potentially overwriting an existing field. Also the body is required to be an object. This is necessary, because our JWT can exceed the size of accept headers because we don't want to limit the number of products your customer has access to.
Caching
The SDK has some build in memory caching. On the server, you might want to tune performance by shipping your own redis caching.