iTeamSSO

Login with iTeam

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.

Os 4 valores que você precisa

issuerhttps://sso.iteam.works
discoveryhttps://sso.iteam.works/.well-known/openid-configuration
client_idgerado no Console
client_secretgerado no Console (só apps com backend)

Requer papel owner ou admin na empresa.

1 · Next.js (Auth.js)

// 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

2 · Node / Express

// 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 })
})

3 · Python (Flask + Authlib)

# 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")}

4 · SPA / Mobile — botão drop-in

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.

5 · Validar o token na sua API

// 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.

Scopes

scopeo que liberacomo 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

Claims do id_token

claimtipodescriçãoexige
substring ID do usuário no iTeam (estável, use como chave)openid
namestring Nome do usuárioprofile
picturestring URL do avatar (quando existe)profile
emailstring E-mailemail
email_verifiedboolean Sempre true (o iTeam valida no cadastro)email
company_idstring Empresa ativa do usuárioiteam:company
company_namestring Nome da empresa ativaiteam:company
rolestring Papel na empresa ativa: owner, admin, member…iteam:company
companiesarray Todas as empresas: [{id,name,role}]iteam:company
areasarray Áreas do usuário: [{id,name}]iteam:areas

Renovação de acesso

# 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.

Endpoints

funçãoURL
Discoveryhttps://sso.iteam.works/.well-known/openid-configuration
Authorizationhttps://sso.iteam.works/oauth2/auth
Tokenhttps://sso.iteam.works/oauth2/token
UserInfohttps://sso.iteam.works/userinfo
JWKShttps://sso.iteam.works/.well-known/jwks.json
Revogaçãohttps://sso.iteam.works/oauth2/revoke
Logouthttps://sso.iteam.works/oauth2/sessions/logout
CSS do botãohttps://sso.iteam.works/sdk/v1/iteam.css
SDK do botãohttps://sso.iteam.works/sdk/v1/iteam.js

Regras que a gente aplica

PKCEObrigatório em clients públicos (SPA/mobile). Recomendado em todos.
Redirect exatoA URI do request precisa ser idêntica a uma registrada — sem wildcard.
ConsentimentoApps de terceiro sempre mostram a tela de permissões. O usuário pode negar.
Secret no browserNunca. Se o código roda no cliente, registre como SPA / Mobile.
🔒 iTeam SSO · OAuth2 / OpenID Connect