OAuth 2.0 + OpenID Connect padrão. Se a sua stack tem uma biblioteca OIDC, ela já funciona — basta apontar para o nosso issuer e o discovery configura o resto.
Requer papel owner ou admin na empresa.
// auth.ts — Auth.js / NextAuth v5
import NextAuth from "next-auth"
export const { handlers, signIn, auth } = NextAuth({
providers: [{
id: "iteam",
name: "iTeam",
type: "oidc",
issuer: "https://sso.iteam.works", // o discovery configura o resto
clientId: process.env.ITEAM_CLIENT_ID,
clientSecret: process.env.ITEAM_CLIENT_SECRET,
authorization: { params: { scope: "openid profile email iteam:company iteam:areas" } },
}],
})
// redirect URI que você registra no Console:
// https://seuapp.com/api/auth/callback/iteam
// Node/Express com openid-client
import * as client from 'openid-client'
const config = await client.discovery(
new URL('https://sso.iteam.works'), // lê /.well-known/openid-configuration
process.env.ITEAM_CLIENT_ID,
process.env.ITEAM_CLIENT_SECRET,
)
app.get('/login', (req, res) => {
const verifier = client.randomPKCECodeVerifier()
req.session.verifier = verifier
const url = client.buildAuthorizationUrl(config, {
redirect_uri: 'https://seuapp.com/callback',
scope: 'openid profile email iteam:company',
code_challenge: await client.calculatePKCECodeChallenge(verifier),
code_challenge_method: 'S256',
})
res.redirect(url.href)
})
app.get('/callback', async (req, res) => {
const tokens = await client.authorizationCodeGrant(config, new URL(req.url, 'https://seuapp.com'), {
pkceCodeVerifier: req.session.verifier,
})
const claims = tokens.claims()
res.json({ email: claims.email, empresa: claims.company_name, papel: claims.role })
})
# Python / Flask com Authlib
from authlib.integrations.flask_client import OAuth
oauth = OAuth(app)
oauth.register(
name="iteam",
server_metadata_url="https://sso.iteam.works/.well-known/openid-configuration",
client_id=os.environ["ITEAM_CLIENT_ID"],
client_secret=os.environ["ITEAM_CLIENT_SECRET"],
client_kwargs={"scope": "openid profile email iteam:company iteam:areas"},
)
@app.route("/login")
def login():
return oauth.iteam.authorize_redirect(url_for("callback", _external=True))
@app.route("/callback")
def callback():
token = oauth.iteam.authorize_access_token()
u = token["userinfo"]
return {"email": u["email"], "empresa": u.get("company_name"), "papel": u.get("role")}
Sem backend e sem escrever PKCE. O iteam.js renderiza o botão da marca,
faz o PKCE, abre o fluxo e devolve os tokens já decodificados.
<!-- SPA / Mobile: client PÚBLICO, sem client_secret, PKCE automático -->
<script src="https://sso.iteam.works/sdk/v1/iteam.js" async></script>
<div class="iteam-signin"
data-client-id="app_xxxxxxxxxxxx"
data-redirect-uri="https://seuapp.com/callback"
data-scope="openid profile email iteam:company iteam:areas"
data-variant="filled"
data-label="Entrar com iTeam"
data-onsuccess="onITeamLogin"></div>
<script>
function onITeamLogin(r) {
// r.tokens.access_token / id_token / refresh_token
// r.user = { sub, name, email, company_id, company_name, role, areas, companies }
console.log('logado:', r.user.email, r.user.company_name, r.user.role)
localStorage.setItem('token', r.tokens.access_token)
}
</script>
Opcionais: data-popup="true" (fluxo em popup), data-variant
(filled · light · dark · outline · soft),
data-size (sm/lg), data-shape="pill",
data-block="true", data-icon-only="true", data-onerror.
Também emite os eventos iteam-sso:success e iteam-sso:error.
// No SEU backend: valide o access_token pela JWKS (RS256).
// Não precisa de segredo compartilhado nem chamar o iTeam a cada request.
import { createRemoteJWKSet, jwtVerify } from 'jose'
const JWKS = createRemoteJWKSet(new URL('https://sso.iteam.works/.well-known/jwks.json'))
export async function verify(bearer) {
const { payload } = await jwtVerify(bearer, JWKS, { issuer: 'https://sso.iteam.works' })
// payload.sub, payload.company_id, payload.role, payload.scp (scopes concedidos)
return payload
}
O access_token é um JWT RS256 — a validação é offline pela JWKS.
Cheque sempre iss, expiração e os scopes em scp antes de liberar a operação.
| scope | o que libera | como aparece pro usuário |
|---|---|---|
openid |
Obrigatório. Emite o id_token com o claim sub. |
Confirmar sua identidade |
profile |
Claims name, picture. |
Seu nome e foto de perfil |
email |
Claims email, email_verified. |
Seu endereço de e-mail |
offline |
Emite refresh_token (adiciona o grant refresh_token). |
Manter você conectado — Renova o acesso sem pedir login de novo |
iteam:company |
Claims company_id, company_name, role, companies[]. |
Sua empresa e seu papel — Nome da empresa e nível de acesso |
iteam:areas |
Claim areas[] — objetos {id,name}. |
Suas áreas — As áreas às quais você pertence |
agents:read |
Autoriza o app a ler os agentes do usuário na API do iTeam. | Ver seus agentes |
| claim | tipo | descrição | exige |
|---|---|---|---|
sub | string | ID do usuário no iTeam (estável, use como chave) | openid |
name | string | Nome do usuário | profile |
picture | string | URL do avatar (quando existe) | profile |
email | string | email | |
email_verified | boolean | Sempre true (o iTeam valida no cadastro) | email |
company_id | string | Empresa ativa do usuário | iteam:company |
company_name | string | Nome da empresa ativa | iteam:company |
role | string | Papel na empresa ativa: owner, admin, member… | iteam:company |
companies | array | Todas as empresas: [{id,name,role}] | iteam:company |
areas | array | Áreas do usuário: [{id,name}] | iteam:areas |
# Renovar o acesso (precisa do scope "offline") curl -X POST https://sso.iteam.works/oauth2/token \ -d grant_type=refresh_token \ -d refresh_token=$REFRESH_TOKEN \ -d client_id=$ITEAM_CLIENT_ID \ -d client_secret=$ITEAM_CLIENT_SECRET
TTL: access_token 15min, refresh_token 30 dias, id_token 1h.
| função | URL |
|---|---|
| Discovery | https://sso.iteam.works/.well-known/openid-configuration |
| Authorization | https://sso.iteam.works/oauth2/auth |
| Token | https://sso.iteam.works/oauth2/token |
| UserInfo | https://sso.iteam.works/userinfo |
| JWKS | https://sso.iteam.works/.well-known/jwks.json |
| Revogação | https://sso.iteam.works/oauth2/revoke |
| Logout | https://sso.iteam.works/oauth2/sessions/logout |
| CSS do botão | https://sso.iteam.works/sdk/v1/iteam.css |
| SDK do botão | https://sso.iteam.works/sdk/v1/iteam.js |