Sign in

Identity

TAAS Identity Solutions is a suite of Identity services. It is a secure and seamless authentication through the user's mobile phone, which will ease the burden of login for your users.

TAAS Identity gives your users a fluid experience across different applications by providing single sign-on. In other words, the users sign in only once and are authenticated on all of their connected applications.

Even cooler, the platform gives your users the opportunity to connect their devices and track their data on their terms. Each user gets an account where they stay in control of everything that is connected and related to them.

Is the service secure? Yes! It is based on the proven standard of OpenID Connect 1.0. You can read more about the standard in the following section.

Getting bored by the documentation already? You can skip right to the get started section.

Different clients have different needs. Luckily, the OpenID Connect standard supports two different flows that a client can choose from to exchange information with the Identity Platform: Authorization Code Flow, and Implicit flow.

While the flows differ to some extent, they all enable users to sign into a service without having to share their password with an application they may not trust.

The following steps will authenticate a user with the Identity Provider, using the Authorization Code Flow:

Steps in the Authorization Code Flow

The Authorization Code Flow is intended for clients that can securely store and transmit a Client Secret to the Identity Provider. The user is only handling the code parameter, and the token exchange is handled between the client backend and the Identity Provider backend using the client secret for authorization.

The authorization code provides a few important security benefits, such as the ability to authenticate the client, and transmission of the access token directly to the client reducing the risk of exposing it to others.

As the client secret should be kept hidden from users, this approach is best suited for web applications with a dedicated backend. Pure front-end JavaScript applications and applications that can be easily decompiled will not gain any value by using a client secret, but we do encourage developers to use the Authorization Code Flow whenever possible.

The following steps will authenticate a user with the Identity Provider, using the Implicit Flow:

Implicit flow no branding

  • The user clicks a sign-in link on your awesome site
  • The user is redirected to the /auth endpoint

    • The user signs in and gives consent. See how we handle the consent process here
  • The user is redirected back to your specified Redirect URI with an Access Token directly using the fragment identifier (#)

Implicit flow is a simple flow with implicit client authentication. In contrast to the other flows, it uses no code or Client Secret. The server identifies a client only to the extent of verifying a valid Redirect URI.

The /auth endpoint returns tokens to the user. The /token endpoint is not used during sign-in or sign-up. This means fewer HTTP requests to authenticate a user.

At the same time, no Client Secret makes this flow less protected against impostors. Only clients that can't keep the Client Secret a secret should use this flow. For example, applications that run in a web browser or on a mobile device.

Error messages

Incorrect redirect_uri

Confirm that the redirect URI you have provided matches the one in the client registration, the authorization endpoint, and the token endpoint.

Code not found

Confirm that you have provided the correct code. In the token endpoint, the code value must match the code value returned from the authorization endpoint.

Code is expired

The code returned from the authorization endpoint is one-time only and usually short lived. Try to get a new code from the authorization endpoint.

Token invalid: Token is not active

The access token returned from the token endpoint has a limited lifespan. Try to go through flow without pauses.

Refresh token expired

Your refresh token has expired.

Stale token

Your token is already used; you have e.g. used the same token in two subsequent requests.

UNKNOWN_CLIENT: Client was not identified by any client authenticator

Confirm that you have an Authorization header in your request with a base64 encoded client id/secret pair.

Unexpected error when authenticating client: java.io.IOException: Bad Base64 input character decimal [x] in array position [y]

Confirm that your Authorization header has a valid base64 encoded client id/secret pair.

Migrating users

In order to make the login process as smooth as possible for your users, we have included a optional way to migrate users on-demand. When registering the client, certain attribute may be set to ensure that we call you in the login process to identify the user.

If no such attributes are registered on your client our default solution is triggered, which gets data from our customer database.

How the user migration works.

We only try to fetch the user profile information the first time the user logs in, given that we don't already know who it is.

If you would like to have your own migration service, it has to support the following:

  • Accept GET-requests
  • Accept a query parameter of phone number. Format: e164
  • Respond with 200 and the types given in Data types
  • Maximum 2 second response time
  • Respond with 404 or similar if phone number can't be found or other failures. An Error object can be attached as payload.

Based on the client's odm_endpoint url, ex. http://somehost/path/to, we try to GET http://somehost/path/to/e164/{msisdn}. The response we expect is given in Data types. {msisdn} is the user's mobile phone number in e164 format.

Advanced attributes include:

  • odmbasicauth_username
  • odmbasicauth_password
  • odm_header_

odm_header_

is a special attribute which can be used if you need Telia Identity to call your migration endpoint with some special headers. Given you have a header named x-headername, registering the odm_header_x-headername combined with the desired value will make us include that header in our request.

Contact the team if you want to enable the migration service for your client. The feature will be included in a self-service application at a later stage.

See a simple application located here.

The following code samples show how we expect the user data to be formatted. Data types: UserResponse, User, Address, and Error.

UserResponse.java

public class UserResponse {

  	@JsonProperty("user")
    private User user;

    @JsonProperty("issuer_id")
    private String issuerId;


    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getIssuerId() {
        return issuerId;
    }

    public void setIssuerId(String issuerId) {
        this.issuerId = issuerId;
    }
}

User.java

public class User {

    @JsonProperty("given_name")
    private String givenName; //"Given name(s) or first name(s) of the End-User.",

    @JsonProperty("family_name")
    private String familyName; //"Surname(s) or last name(s) of the End-User.",

    @JsonProperty("middle_name")
    private String middleName; //"Middle name(s) of the End-User.",

    @JsonProperty("nickname")
    private String nickname; //"Casual name of the End-User that may or may not be the same as the given_name.",

    @JsonProperty("preferred_username")
    private String preferredUsername; //"Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe.",

    @JsonProperty("profile")
    private String profile; //"URL of the End-User's profile page.",

    @JsonProperty("picture")
    private String picture; //"URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image.",

    @JsonProperty("website")
    private String website; //"URL of the End-User's Web page or blog.",

    @JsonProperty("email")
    private String email; //"End-User's email address",

    @JsonProperty("gender")
    private String gender; //"End-User's gender. Values defined by this specification are 'female' and 'male'.",

    @JsonProperty("birthdate")
    private String birthdate; //"End-User's birthday, represented as an ISO 8601:2004 YYYY-MM-DD format.",

    @JsonProperty("phone_number")
    private String phoneNumber; // "End-User's preferred telephone number. E.164.",

    @JsonProperty("address")
    private Address address;

    @JsonProperty("external_id")
    private String externalId; //"End-User id at the federated source",

    @JsonProperty("preferred_locale")
    private String preferredLocale; //"IETF, BCP47 locale",
  
  	public String getGivenName() {
        return givenName;
    }

    public void setGivenName(String givenName) {
        this.givenName = givenName;
    }

    public String getFamilyName() {
        return familyName;
    }

    public void setFamilyName(String familyName) {
        this.familyName = familyName;
    }

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getPreferredUsername() {
        return preferredUsername;
    }

    public void setPreferredUsername(String preferredUsername) {
        this.preferredUsername = preferredUsername;
    }

    public String getProfile() {
        return profile;
    }

    public void setProfile(String profile) {
        this.profile = profile;
    }

    public String getPicture() {
        return picture;
    }

    public void setPicture(String picture) {
        this.picture = picture;
    }

    public String getWebsite() {
        return website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getBirthdate() {
        return birthdate;
    }

    public void setBirthdate(String birthdate) {
        this.birthdate = birthdate;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getExternalId() {
        return externalId;
    }

    public void setExternalId(String externalId) {
        this.externalId = externalId;
    }

    public String getPreferredLocale() {
        return preferredLocale;
    }

    public void setPreferredLocale(String preferredLocale) {
        this.preferredLocale = preferredLocale;
    }
}

Address.java

public class Address {

    @JsonProperty("street_address")
    private String streetAddress;

    @JsonProperty("locality")
    private String locality;

    @JsonProperty("region")
    private String region;

    @JsonProperty("postal_code")
    private String postalCode;

    @JsonProperty("country")
    private String countryCode; // ISO 3166-1 alpha-2. Two letter country code

    public String getStreetAddress() {
        return streetAddress;
    }

    public void setStreetAddress(String streetAddress) {
        this.streetAddress = streetAddress;
    }

    public String getLocality() {
        return locality;
    }

    public void setLocality(String locality) {
        this.locality = locality;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public String getPostalCode() {
        return postalCode;
    }

    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }
}

Error.java

public class Error {
  
    @JsonProperty("error")
    private String error;

    public Error(String error) { this.error = error; }

    public String getError() {
        return error;
    }
}

OpenID Connect

OpenID Connect is a layer built on top of the OAuth protocol for authenticating and authorizing users using JSON-based Web Tokens (JWT). In OpenID Connect, authorization is handled just as in OAuth 2.0, meaning applications request Access Tokens and Refresh Tokens from a dedicated service. Such a service is called an Identity Provider and is typically used by Clients or a Relying Party. Furthermore, in OpenID Connect, an Identity Provider can return an Identity Token, which carries information about the identity of the user.

In OpenID Connect, there are three different authentication flows. However, they all start with the client redirecting the user to the Identity Provider. This reduces the number of entities a user must trust. Even better, it helps keep their account secure. To summarize, the following generally holds true regardless of which flow is used:

  • The client redirects the user to the Identity Provider
  • The user authenticates with the Identity Provider
  • The Identity Provider returns a set of data to a predefined callback endpoint on the client

There are three types of tokens used in OpenID Connect. They are received by exchanging an authorization code at the /token endpoint

Access Tokens

An Access Token (JSON Web Token) is an opaque string that states what actions a client may invoke on the Identity Provider for a given time. A parameter called scope controls the set of resources and operations that an Access Token permits. When requesting an Access Token, the client sends one or more values in the scope parameter.

Access Tokens are unique and should be stored securely.

Best practice is to request scopes incrementally, which means at the time access is required, rather than up front.

Refresh Tokens

A Refresh Token is used by the client to get a new Access Token from the Identity Provider when the current becomes invalid or expires.

Obtaining new tokens by using the refresh_token should only happen if the ID Token has expired.

Identity Tokens

The Identity Token or ID Token is a JSON Web Token, which is used to identify the user. If you decode this token, it is possible to see that it contains several claims.

It is important to validate the ID Token before you use the information for anything or rely on it as an assertion that the user has authenticated.

User attributes

This list of user attributes defines a set of OpenID Connect standard Claims. They can be requested to be returned either in the UserInfo Response or in the ID Token.

Member type Description
sub string Subject - Identifier for the End-User at the Issuer.
given_name string Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.
family_name string Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.
email string End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax. The client MUST NOT rely upon this value being unique.
email_verified boolean True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OpenID provider took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.
gender string End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable.
birthdate string End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. Note that depending on the underlying platform's date related function, providing just year can result in varying month and day, so the implementers need to take this factor into account to correctly process the dates.
zoneinfo string String from zoneinfo [zoneinfo] time zone database representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles.
locale string End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Clients MAY choose to accept this locale syntax as well.
phone_number string End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension, it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.
phonenumberverified boolean True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OpenID provider took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.
address JSON object End-User's preferred postal address. The value of the address member is a JSON [RFC4627] structure.
updated_at number Time the End-User's information was last updated. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.

For more detailed information and the full list of claims supported by OpenID Connect, see the specification.

User IDs and PCRs

What is the PCR? PCR stands for "Pseudonymous Customer Reference". This is a unique identifier that Mobile Connect uses to reference an end user. The PCR is a unique id that always represents a specific user. Hence pseudonymous.

Why use the PCR? By leveraging the PCR, the user's privacy is protected while the service provider is assured that this is an actual user. Developers can then request additional information about users with their consent. This allows users to be confident that their personal information will only be shared with their explicit consent.

Is the PCR secure? A PCR is unique to each application and user combined. Other Mobile Connect-enabled service providers will not be able to copy and use your application's PCR in their system. In the unlikely event third parties gained access to a PCR, the user’s information would not be compromised.

The PCR that you receive for mobile number A on Application A will not be the same as that created for Mobile number A on Application B.