Zaloguj przez 1g.pl
Wstaw na swojej stronie jeden przycisk — a Twoi użytkownicy zalogują się kontem z 1g.pl, bez zakładania u Ciebie kolejnego hasła. Dostajesz imię i nazwisko, adres e-mail i awatar. Działa dokładnie tak, jak przycisk Google: standard OAuth 2.0, kod autoryzacyjny z PKCE.
Zaloguj przez 1g.pl
Załóż aplikację i odbierz client_id
- Zarejestruj aplikację — dostajesz
client_idi sekret - Wstaw przycisk — przenosi użytkownika do nas
- Wymień kod na token — po stronie swojego serwera
- Pobierz dane — imię, e-mail, awatar
Zarejestruj aplikację
Wejdź w Moje integracje i podaj nazwę oraz adres powrotu — stronę u siebie, na którą Cię odeślemy po zalogowaniu. W zamian dostajesz dwie rzeczy:
| Co | Gdzie to trzymać |
|---|---|
client_id32 znaki | Jawny. Wpisujesz go w kod strony, widzi go każdy — i tak ma być. |
client_secret43 znaki | Wyłącznie na Twoim serwerze. Nigdy w kodzie strony, nigdy w aplikacji mobilnej, nigdy w repozytorium. Pokazujemy go raz — u nas leży tylko jego odcisk, więc drugi raz go nie odzyskasz. |
https://twoja.pl/odbior i
https://twoja.pl/odbior/ to dla nas dwa różne adresy.
Porównanie „zaczyna się od" pozwoliłoby komuś, kto zna Twój client_id,
dopisać własną ścieżkę i przejąć kod logowania — dlatego go nie stosujemy.
Wymagamy HTTPS; wyjątkiem jest localhost, żeby dało się
pracować na własnym komputerze.
Wstaw przycisk
Najprostsza wersja to zwykły odnośnik. Podmień TWOJ_CLIENT_ID
i adres powrotu na swoje:
<a class="logowanie-1g"
href="https://1g.pl/api/oauth/authorize.php
?client_id=TWOJ_CLIENT_ID
&redirect_uri=https%3A%2F%2Ftwoja.pl%2Fodbior
&response_type=code
&scope=profil%20email
&state=LOSOWY_CIAG_Z_SESJI">
<img src="https://1g.pl/assets/img/logowanie-1g.svg" alt="" width="20" height="20">
Zaloguj przez 1g.pl
</a>Wygląd przycisku — do wklejenia w swój arkusz stylów:
.logowanie-1g {
display: inline-flex; align-items: center; gap: 9px;
padding: 11px 20px; min-height: 44px;
border: 1px solid #d0d5dd; border-radius: 9px;
background: #fff; color: #1a202c;
font: 600 15px/1 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
text-decoration: none;
}
.logowanie-1g:hover { background: #f7f9fc; }Parametry adresu
| Parametr | Wymagany | Znaczenie |
|---|---|---|
client_id | tak | Z panelu „Moje integracje”. |
redirect_uri | tak | Jeden z adresów powrotu podanych przy rejestracji, zakodowany (urlencode). |
response_type | tak | Zawsze code. Innego przepływu świadomie nie wystawiamy. |
scope | nie | profil (imię, nazwisko, awatar), email. Rozdzielone spacją. Domyślnie oba. |
state | zalecany | Losowy ciąg, który zapisujesz w sesji użytkownika. Oddamy go bez zmian — porównaj po powrocie. |
code_challenge | zalecany | PKCE, patrz niżej. |
code_challenge_method | z PKCE | S256 albo plain. Używaj S256. |
state
Bez niego ktoś może podstawić Twojemu zalogowanemu użytkownikowi własny kod
autoryzacyjny i połączyć jego konto u Ciebie ze swoim kontem w 1g.pl. Losowy ciąg
w sesji i porównanie po powrocie zamykają tę drogę. To trzy linijki kodu.
Wymień kod na token
Wracamy na Twój adres powrotu z parametrami code
i state. Kod żyje 60 sekund i działa
raz. Wymieniasz go na token — i to musi się dziać na Twoim serwerze,
bo dopiero tu pojawia się sekret.
<?php
session_start();
const CLIENT_ID = 'TWOJ_CLIENT_ID';
const CLIENT_SECRET = 'TWOJ_SEKRET'; // tylko tutaj, nigdy w kodzie strony
const REDIRECT_URI = 'https://twoja.pl/odbior.php';
/* ── Wyjście: przenosimy użytkownika do 1g.pl ─────────────────────────── */
if (!isset($_GET['code'])) {
$_SESSION['1g_state'] = bin2hex(random_bytes(16));
$_SESSION['1g_verifier'] = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
$wyzwanie = rtrim(strtr(base64_encode(
hash('sha256', $_SESSION['1g_verifier'], true)), '+/', '-_'), '=');
header('Location: https://1g.pl/api/oauth/authorize.php?' . http_build_query([
'client_id' => CLIENT_ID,
'redirect_uri' => REDIRECT_URI,
'response_type' => 'code',
'scope' => 'profil email',
'state' => $_SESSION['1g_state'],
'code_challenge' => $wyzwanie,
'code_challenge_method' => 'S256',
]));
exit;
}
/* ── Powrót: najpierw state, potem cokolwiek innego ───────────────────── */
if (!hash_equals($_SESSION['1g_state'] ?? '', $_GET['state'] ?? '')) {
http_response_code(400);
exit('Niezgodny state — to logowanie nie zaczęło się u nas.');
}
/* ── Kod → token (serwer–serwer) ──────────────────────────────────────── */
$ch = curl_init('https://1g.pl/api/oauth/token.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => REDIRECT_URI, // ten sam co wyżej, co do znaku
'client_id' => CLIENT_ID,
'client_secret' => CLIENT_SECRET,
'code_verifier' => $_SESSION['1g_verifier'],
]),
]);
$odp = json_decode((string)curl_exec($ch), true);
curl_close($ch);
if (empty($odp['access_token'])) {
http_response_code(400);
exit('Logowanie nieudane: ' . ($odp['error_description'] ?? $odp['error'] ?? '?'));
}
/* ── Token → dane użytkownika ─────────────────────────────────────────── */
$ch = curl_init('https://1g.pl/api/oauth/userinfo.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $odp['access_token']],
]);
$user = json_decode((string)curl_exec($ch), true);
curl_close($ch);
/* $user['sub'] to stały numer konta w 1g.pl — po nim rozpoznajesz powracającego.
Nie po e-mailu: e-mail wolno zmienić, numer nie. */
$_SESSION['moj_uzytkownik'] = $user;
header('Location: /');import crypto from 'node:crypto';
const CLIENT_ID = 'TWOJ_CLIENT_ID';
const CLIENT_SECRET = process.env.CLIENT_SECRET_1G; // ze zmiennej środowiskowej
const REDIRECT_URI = 'https://twoja.pl/odbior';
const b64url = (buf) => buf.toString('base64url');
// Wyjście
export function adresLogowania(sesja) {
sesja.state = b64url(crypto.randomBytes(16));
sesja.verifier = b64url(crypto.randomBytes(32));
const wyzwanie = b64url(crypto.createHash('sha256').update(sesja.verifier).digest());
const p = new URLSearchParams({
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
response_type: 'code',
scope: 'profil email',
state: sesja.state,
code_challenge: wyzwanie,
code_challenge_method: 'S256',
});
return `https://1g.pl/api/oauth/authorize.php?${p}`;
}
// Powrót
export async function odbior(sesja, query) {
if (query.state !== sesja.state) throw new Error('Niezgodny state');
const odp = await fetch('https://1g.pl/api/oauth/token.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: query.code,
redirect_uri: REDIRECT_URI,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code_verifier: sesja.verifier,
}),
}).then(r => r.json());
if (!odp.access_token) throw new Error(odp.error_description || odp.error);
return fetch('https://1g.pl/api/oauth/userinfo.php', {
headers: { Authorization: `Bearer ${odp.access_token}` },
}).then(r => r.json());
}PKCE — po co ta dodatkowa para
Losujesz code_verifier, wysyłasz do nas jego skrót
(code_challenge), a przy wymianie kodu pokazujesz
oryginał. Kto przechwyci sam kod — z historii przeglądarki, z nagłówka odsyłającego,
z dziennika pośrednika — nie wymieni go bez tej wartości, bo nigdy nie opuściła
Twojego serwera. U nas PKCE jest nieobowiązkowe, ale kod przysłany z wyzwaniem
wymaga weryfikatora przy wymianie — nie da się go po drodze pominąć.
Co dostajesz
{
"sub": "84213",
"iss": "https://1g.pl",
"name": "Anna Kowalska",
"given_name": "Anna",
"family_name": "Kowalska",
"picture": "https://cdn.1g.pl/avatars/84213.webp",
"email": "anna.kowalska@example.com",
"email_verified": true
}| Pole | Zakres | Uwagi |
|---|---|---|
sub | zawsze | Stały numer konta. Po nim rozpoznajesz powracającego — e-mail wolno zmienić, ten numer nie. |
iss | zawsze | Kto wystawił: https://1g.pl. |
name, given_name, family_name | profil | Może być puste, jeśli użytkownik nie uzupełnił profilu. |
picture | profil | null, gdy nie ma awatara. |
email | email | Adres z konta. |
email_verified | email | Mówimy wprost, czy potwierdziliśmy ten adres. |
email
Jeśli email_verified jest false,
a Ty łączysz konta po adresie, ktoś może wpisać cudzy e-mail u nas i wejść na cudze
konto u Ciebie. Łącz po sub, a e-mail traktuj jako
dane pomocnicze.
Adresy punktów
| Punkt | Metoda | Do czego |
|---|---|---|
https://1g.pl/api/oauth/authorize.php | GET | Ekran zgody. Tu przenosisz użytkownika. |
https://1g.pl/api/oauth/token.php | POST | Kod → token. Serwer–serwer. Sekret w polu formularza albo w nagłówku Authorization: Basic. |
https://1g.pl/api/oauth/userinfo.php | GET | Dane konta. Token wyłącznie w nagłówku Authorization: Bearer. |
Ważność
| Co | Jak długo | Dlaczego tyle |
|---|---|---|
| kod autoryzacyjny | 60 sekund, jednorazowy | Tyle trwa przekierowanie i wymiana. Dłużej znaczyłoby tylko szersze okno dla kogoś, kto go podejrzy. |
| token dostępu | 30 dni | Aplikacja pobiera dane przy logowaniu, nie w kółko. Cofnięcie zgody unieważnia token natychmiast, nie po 30 dniach. |
Błędy
| Kod | Co się stało |
|---|---|
invalid_client | Nieznany client_id albo zły sekret. Odpowiedź jest ta sama w obu wypadkach — celowo, żeby nie dało się sprawdzać, które identyfikatory istnieją. |
invalid_grant | Kod wygasł, został już użyty, przyszedł z innym redirect_uri albo bez poprawnego code_verifier. |
invalid_scope | Prosisz o zakres, którego nie ma w rejestracji aplikacji. |
invalid_token | Token wygasł, został odwołany albo konto przestało istnieć. Zacznij logowanie od nowa. |
unsupported_response_type | Coś innego niż code. Przepływu z tokenem w adresie nie wystawiamy. |
| ekran błędu u nas, bez powrotu | Nieznany client_id albo niezarejestrowany redirect_uri. Nie odsyłamy wtedy nigdzie — odesłanie pod niesprawdzony adres byłoby przekazaniem kodu obcemu. |
Częste pytania
Sekret mi wyciekł. Co teraz?
W panelu Moje integracje kliknij „Nowy sekret”. Stary przestaje działać w tej samej chwili — do czasu wgrania nowego u siebie nikt się nie zaloguje, ale osoby już zalogowane zostają, bo ich tokeny są od sekretu niezależne.
Mogę użyć tego w aplikacji mobilnej?
Tak, ale sekret musi zostać na Twoim serwerze — w aplikacji zainstalowanej na cudzym telefonie nic nie jest tajne. Aplikacja otwiera nasz ekran w przeglądarce systemowej, a wymianę kodu na token wykonuje Twoje zaplecze. PKCE jest wtedy obowiązkowe w praktyce, nie tylko z nazwy.
Użytkownik cofnął zgodę — dowiem się o tym?
Nie powiadamiamy o tym osobno. Zorientujesz się przy pierwszym zapytaniu o dane:
dostaniesz invalid_token ze statusem 401. Wtedy
po prostu poproś użytkownika o ponowne zalogowanie.
Ile aplikacji mogę mieć?
Dziesięć na jedno konto. Jeśli potrzebujesz więcej, napisz do nas.